blob: a466e2e478e83f196f5c4eafc7f2bee39757ebec [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 Ahernb1d40992019-04-16 14:35:59 -07001133 trace_fib6_table_lookup(net, res.f6i, 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 */
1842struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
1843 int oif, struct flowi6 *fl6, 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;
David Ahernb7bc4b62019-04-16 14:36:08 -07001846 struct fib6_result res;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847
David Ahern64547432018-05-09 20:34:19 -07001848 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001849 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
David Ahernca254492015-10-12 11:47:10 -07001851 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1852 oif = 0;
1853
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001854redo_rt6_select:
David Ahernb7bc4b62019-04-16 14:36:08 -07001855 rt6_select(net, fn, oif, &res, strict);
1856 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001857 fn = fib6_backtrack(fn, &fl6->saddr);
1858 if (fn)
1859 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001860 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1861 /* also consider unreachable route */
1862 strict &= ~RT6_LOOKUP_F_REACHABLE;
1863 fn = saved_fn;
1864 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001865 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001866 }
1867
David Ahernb7bc4b62019-04-16 14:36:08 -07001868 trace_fib6_table_lookup(net, res.f6i, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001869
David Ahernb7bc4b62019-04-16 14:36:08 -07001870 return res.f6i;
David Ahern1d053da2018-05-09 20:34:21 -07001871}
1872
1873struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1874 int oif, struct flowi6 *fl6,
1875 const struct sk_buff *skb, int flags)
1876{
David Ahernb1d40992019-04-16 14:35:59 -07001877 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001878 struct rt6_info *rt;
1879 int strict = 0;
1880
1881 strict |= flags & RT6_LOOKUP_F_IFACE;
1882 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1883 if (net->ipv6.devconf_all->forwarding == 0)
1884 strict |= RT6_LOOKUP_F_REACHABLE;
1885
1886 rcu_read_lock();
1887
David Ahernb1d40992019-04-16 14:35:59 -07001888 res.f6i = fib6_table_lookup(net, table, oif, fl6, strict);
1889 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001890 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001891 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001892 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001893 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001894 }
1895
David Ahernb1d40992019-04-16 14:35:59 -07001896 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001897
David Ahern23fb93a2018-04-17 17:33:23 -07001898 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001899 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001900 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001901 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001902 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001903
Wei Wang66f5d6c2017-10-06 12:06:10 -07001904 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001905 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001906 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001907 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001908 /* Create a RTF_CACHE clone which will not be
1909 * owned by the fib6 tree. It is for the special case where
1910 * the daddr in the skb during the neighbor look-up is different
1911 * from the fl6->daddr used to look-up route here.
1912 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001913 struct rt6_info *uncached_rt;
1914
David Ahern85bd05d2019-04-16 14:36:01 -07001915 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001916
David Ahern4d85cd02018-04-20 15:37:59 -07001917 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001918
Wei Wang1cfb71e2017-06-17 10:42:33 -07001919 if (uncached_rt) {
1920 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1921 * No need for another dst_hold()
1922 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001923 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001924 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001925 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001926 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001927 dst_hold(&uncached_rt->dst);
1928 }
David Ahernb8115802015-11-19 12:24:22 -08001929
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001930 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001931 } else {
1932 /* Get a percpu copy */
1933
1934 struct rt6_info *pcpu_rt;
1935
Eric Dumazet951f7882017-10-08 21:07:18 -07001936 local_bh_disable();
David Aherndb3fede2019-04-16 14:36:03 -07001937 pcpu_rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001938
David Ahern93531c62018-04-17 17:33:25 -07001939 if (!pcpu_rt)
David Aherndb3fede2019-04-16 14:36:03 -07001940 pcpu_rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07001941
Eric Dumazet951f7882017-10-08 21:07:18 -07001942 local_bh_enable();
1943 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001944
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001945 return pcpu_rt;
1946 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001947}
David Ahern9ff74382016-06-13 13:44:19 -07001948EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001949
David Ahernb75cc8f2018-03-02 08:32:17 -08001950static struct rt6_info *ip6_pol_route_input(struct net *net,
1951 struct fib6_table *table,
1952 struct flowi6 *fl6,
1953 const struct sk_buff *skb,
1954 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001955{
David Ahernb75cc8f2018-03-02 08:32:17 -08001956 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001957}
1958
Mahesh Bandeward409b842016-09-16 12:59:08 -07001959struct dst_entry *ip6_route_input_lookup(struct net *net,
1960 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001961 struct flowi6 *fl6,
1962 const struct sk_buff *skb,
1963 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001964{
1965 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1966 flags |= RT6_LOOKUP_F_IFACE;
1967
David Ahernb75cc8f2018-03-02 08:32:17 -08001968 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001969}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001970EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001971
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001972static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001973 struct flow_keys *keys,
1974 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001975{
1976 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1977 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001978 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001979 const struct ipv6hdr *inner_iph;
1980 const struct icmp6hdr *icmph;
1981 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001982 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001983
1984 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1985 goto out;
1986
Eric Dumazetcea67a22018-04-29 09:54:59 -07001987 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1988 sizeof(_icmph), &_icmph);
1989 if (!icmph)
1990 goto out;
1991
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001992 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1993 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1994 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1995 icmph->icmp6_type != ICMPV6_PARAMPROB)
1996 goto out;
1997
1998 inner_iph = skb_header_pointer(skb,
1999 skb_transport_offset(skb) + sizeof(*icmph),
2000 sizeof(_inner_iph), &_inner_iph);
2001 if (!inner_iph)
2002 goto out;
2003
2004 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002005 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002006out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002007 if (_flkeys) {
2008 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
2009 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
2010 keys->tags.flow_label = _flkeys->tags.flow_label;
2011 keys->basic.ip_proto = _flkeys->basic.ip_proto;
2012 } else {
2013 keys->addrs.v6addrs.src = key_iph->saddr;
2014 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002015 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002016 keys->basic.ip_proto = key_iph->nexthdr;
2017 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002018}
2019
2020/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002021u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2022 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002023{
2024 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002025 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002026
David S. Millerbbfa0472018-03-12 11:09:33 -04002027 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002028 case 0:
2029 memset(&hash_keys, 0, sizeof(hash_keys));
2030 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2031 if (skb) {
2032 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2033 } else {
2034 hash_keys.addrs.v6addrs.src = fl6->saddr;
2035 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002036 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002037 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2038 }
2039 break;
2040 case 1:
2041 if (skb) {
2042 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2043 struct flow_keys keys;
2044
2045 /* short-circuit if we already have L4 hash present */
2046 if (skb->l4_hash)
2047 return skb_get_hash_raw(skb) >> 1;
2048
2049 memset(&hash_keys, 0, sizeof(hash_keys));
2050
2051 if (!flkeys) {
2052 skb_flow_dissect_flow_keys(skb, &keys, flag);
2053 flkeys = &keys;
2054 }
2055 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2056 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2057 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2058 hash_keys.ports.src = flkeys->ports.src;
2059 hash_keys.ports.dst = flkeys->ports.dst;
2060 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2061 } else {
2062 memset(&hash_keys, 0, sizeof(hash_keys));
2063 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2064 hash_keys.addrs.v6addrs.src = fl6->saddr;
2065 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2066 hash_keys.ports.src = fl6->fl6_sport;
2067 hash_keys.ports.dst = fl6->fl6_dport;
2068 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2069 }
2070 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002071 }
David Ahern9a2a5372018-03-02 08:32:15 -08002072 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002073
David Ahern9a2a5372018-03-02 08:32:15 -08002074 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002075}
2076
Thomas Grafc71099a2006-08-04 23:20:06 -07002077void ip6_route_input(struct sk_buff *skb)
2078{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002079 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002080 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002081 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002082 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002083 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002084 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002085 .daddr = iph->daddr,
2086 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002087 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002088 .flowi6_mark = skb->mark,
2089 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002090 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002091 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002092
Jiri Benc904af042015-08-20 13:56:31 +02002093 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002094 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002095 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002096
2097 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2098 flkeys = &_flkeys;
2099
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002100 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002101 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002102 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002103 skb_dst_set(skb,
2104 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002105}
2106
David Ahernb75cc8f2018-03-02 08:32:17 -08002107static struct rt6_info *ip6_pol_route_output(struct net *net,
2108 struct fib6_table *table,
2109 struct flowi6 *fl6,
2110 const struct sk_buff *skb,
2111 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002112{
David Ahernb75cc8f2018-03-02 08:32:17 -08002113 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002114}
2115
Paolo Abeni6f21c962016-01-29 12:30:19 +01002116struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2117 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002118{
David Ahernd46a9d62015-10-21 08:42:22 -07002119 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002120
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002121 if (ipv6_addr_type(&fl6->daddr) &
2122 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002123 struct dst_entry *dst;
2124
2125 dst = l3mdev_link_scope_lookup(net, fl6);
2126 if (dst)
2127 return dst;
2128 }
David Ahernca254492015-10-12 11:47:10 -07002129
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002130 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002131
David Ahernd46a9d62015-10-21 08:42:22 -07002132 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002133 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002134 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002135 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002136
David Ahernd46a9d62015-10-21 08:42:22 -07002137 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002138 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002139 else if (sk)
2140 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002141
David Ahernb75cc8f2018-03-02 08:32:17 -08002142 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002144EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145
David S. Miller2774c132011-03-01 14:59:04 -08002146struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002147{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002148 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002149 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002150 struct dst_entry *new = NULL;
2151
Wei Wang1dbe32522017-06-17 10:42:26 -07002152 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002153 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002154 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002155 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002156 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002157
Changli Gaod8d1f302010-06-10 23:31:35 -07002158 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002159 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002160 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002161 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002162
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002163 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002164
Wei Wang1dbe32522017-06-17 10:42:26 -07002165 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002166 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002167 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002168
2169 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2170#ifdef CONFIG_IPV6_SUBTREES
2171 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2172#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002173 }
2174
David S. Miller69ead7a2011-03-01 14:45:33 -08002175 dst_release(dst_orig);
2176 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002177}
David S. Miller14e50e52007-05-24 18:17:54 -07002178
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179/*
2180 * Destination cache support functions
2181 */
2182
David Ahern8d1c8022018-04-17 17:33:26 -07002183static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002184{
Steffen Klassert36143642017-08-25 09:05:42 +02002185 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002186
David Ahern8ae86972018-04-20 15:38:03 -07002187 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002188 return false;
2189
2190 if (fib6_check_expired(f6i))
2191 return false;
2192
2193 return true;
2194}
2195
David Aherna68886a2018-04-20 15:38:02 -07002196static struct dst_entry *rt6_check(struct rt6_info *rt,
2197 struct fib6_info *from,
2198 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002199{
Wei Wangc5cff852017-08-21 09:47:10 -07002200 u32 rt_cookie = 0;
2201
David Aherna68886a2018-04-20 15:38:02 -07002202 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002203 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002204 return NULL;
2205
2206 if (rt6_check_expired(rt))
2207 return NULL;
2208
2209 return &rt->dst;
2210}
2211
David Aherna68886a2018-04-20 15:38:02 -07002212static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2213 struct fib6_info *from,
2214 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002215{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002216 if (!__rt6_check_expired(rt) &&
2217 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002218 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002219 return &rt->dst;
2220 else
2221 return NULL;
2222}
2223
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2225{
David Aherna87b7dc2018-04-20 15:38:00 -07002226 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002227 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 struct rt6_info *rt;
2229
David Aherna87b7dc2018-04-20 15:38:00 -07002230 rt = container_of(dst, struct rt6_info, dst);
2231
2232 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002234 /* All IPV6 dsts are created with ->obsolete set to the value
2235 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2236 * into this function always.
2237 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002238
David Aherna68886a2018-04-20 15:38:02 -07002239 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002240
David Aherna68886a2018-04-20 15:38:02 -07002241 if (from && (rt->rt6i_flags & RTF_PCPU ||
2242 unlikely(!list_empty(&rt->rt6i_uncached))))
2243 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002244 else
David Aherna68886a2018-04-20 15:38:02 -07002245 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002246
2247 rcu_read_unlock();
2248
2249 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250}
2251
2252static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2253{
2254 struct rt6_info *rt = (struct rt6_info *) dst;
2255
2256 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002257 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002258 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002259 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002260 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002261 dst = NULL;
2262 }
David Ahernc3c14da2018-04-23 11:32:06 -07002263 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002264 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002266 dst = NULL;
2267 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002269 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270}
2271
2272static void ip6_link_failure(struct sk_buff *skb)
2273{
2274 struct rt6_info *rt;
2275
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002276 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277
Eric Dumazetadf30902009-06-02 05:19:30 +00002278 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002280 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002281 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002282 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002283 } else {
David Aherna68886a2018-04-20 15:38:02 -07002284 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002285 struct fib6_node *fn;
2286
David Aherna68886a2018-04-20 15:38:02 -07002287 from = rcu_dereference(rt->from);
2288 if (from) {
2289 fn = rcu_dereference(from->fib6_node);
2290 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2291 fn->fn_sernum = -1;
2292 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002293 }
David Ahern8a14e462018-04-23 11:32:07 -07002294 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 }
2296}
2297
David Ahern6a3e0302018-04-20 15:37:57 -07002298static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2299{
David Aherna68886a2018-04-20 15:38:02 -07002300 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2301 struct fib6_info *from;
2302
2303 rcu_read_lock();
2304 from = rcu_dereference(rt0->from);
2305 if (from)
2306 rt0->dst.expires = from->expires;
2307 rcu_read_unlock();
2308 }
David Ahern6a3e0302018-04-20 15:37:57 -07002309
2310 dst_set_expires(&rt0->dst, timeout);
2311 rt0->rt6i_flags |= RTF_EXPIRES;
2312}
2313
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002314static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2315{
2316 struct net *net = dev_net(rt->dst.dev);
2317
David Ahernd4ead6b2018-04-17 17:33:16 -07002318 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002319 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002320 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2321}
2322
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002323static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2324{
2325 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002326 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002327}
2328
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002329static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2330 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002332 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002333 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
Xin Long19bda362016-10-28 18:18:01 +08002335 if (dst_metric_locked(dst, RTAX_MTU))
2336 return;
2337
Julian Anastasov0dec8792017-02-06 23:14:16 +02002338 if (iph) {
2339 daddr = &iph->daddr;
2340 saddr = &iph->saddr;
2341 } else if (sk) {
2342 daddr = &sk->sk_v6_daddr;
2343 saddr = &inet6_sk(sk)->saddr;
2344 } else {
2345 daddr = NULL;
2346 saddr = NULL;
2347 }
2348 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002349 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2350 if (mtu >= dst_mtu(dst))
2351 return;
David S. Miller81aded22012-06-15 14:54:11 -07002352
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002353 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002354 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002355 /* update rt6_ex->stamp for cache */
2356 if (rt6->rt6i_flags & RTF_CACHE)
2357 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002358 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002359 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002360 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002361
David Ahern4d85cd02018-04-20 15:37:59 -07002362 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002363 res.f6i = rcu_dereference(rt6->from);
2364 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002365 rcu_read_unlock();
2366 return;
2367 }
David Ahern85bd05d2019-04-16 14:36:01 -07002368 res.nh = &res.f6i->fib6_nh;
2369 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002370 if (nrt6) {
2371 rt6_do_update_pmtu(nrt6, mtu);
David Ahern5012f0a2019-04-16 14:36:05 -07002372 if (rt6_insert_exception(nrt6, &res))
Wei Wang2b760fc2017-10-06 12:06:03 -07002373 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002374 }
David Aherna68886a2018-04-20 15:38:02 -07002375 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 }
2377}
2378
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002379static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2380 struct sk_buff *skb, u32 mtu)
2381{
2382 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2383}
2384
David S. Miller42ae66c2012-06-15 20:01:57 -07002385void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002386 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002387{
2388 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2389 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002390 struct flowi6 fl6 = {
2391 .flowi6_oif = oif,
2392 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2393 .daddr = iph->daddr,
2394 .saddr = iph->saddr,
2395 .flowlabel = ip6_flowinfo(iph),
2396 .flowi6_uid = uid,
2397 };
David S. Miller81aded22012-06-15 14:54:11 -07002398
2399 dst = ip6_route_output(net, NULL, &fl6);
2400 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002401 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002402 dst_release(dst);
2403}
2404EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2405
2406void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2407{
David Ahern7ddacfa2018-11-18 10:45:30 -08002408 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002409 struct dst_entry *dst;
2410
David Ahern7ddacfa2018-11-18 10:45:30 -08002411 if (!oif && skb->dev)
2412 oif = l3mdev_master_ifindex(skb->dev);
2413
2414 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002415
2416 dst = __sk_dst_get(sk);
2417 if (!dst || !dst->obsolete ||
2418 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2419 return;
2420
2421 bh_lock_sock(sk);
2422 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2423 ip6_datagram_dst_update(sk, false);
2424 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002425}
2426EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2427
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002428void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2429 const struct flowi6 *fl6)
2430{
2431#ifdef CONFIG_IPV6_SUBTREES
2432 struct ipv6_pinfo *np = inet6_sk(sk);
2433#endif
2434
2435 ip6_dst_store(sk, dst,
2436 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2437 &sk->sk_v6_daddr : NULL,
2438#ifdef CONFIG_IPV6_SUBTREES
2439 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2440 &np->saddr :
2441#endif
2442 NULL);
2443}
2444
David Ahern9b6b35a2019-04-16 14:36:02 -07002445static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002446 struct flowi6 *fl6,
2447 const struct in6_addr *gw,
2448 struct rt6_info **ret)
2449{
David Ahern9b6b35a2019-04-16 14:36:02 -07002450 const struct fib6_nh *nh = res->nh;
2451
David Ahern0b34eb02019-04-09 14:41:19 -07002452 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2453 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2454 return false;
2455
2456 /* rt_cache's gateway might be different from its 'parent'
2457 * in the case of an ip redirect.
2458 * So we keep searching in the exception table if the gateway
2459 * is different.
2460 */
2461 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2462 struct rt6_info *rt_cache;
2463
David Ahern9b6b35a2019-04-16 14:36:02 -07002464 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002465 if (rt_cache &&
2466 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2467 *ret = rt_cache;
2468 return true;
2469 }
2470 return false;
2471 }
2472 return true;
2473}
2474
Duan Jiongb55b76b2013-09-04 19:44:21 +08002475/* Handle redirects */
2476struct ip6rd_flowi {
2477 struct flowi6 fl6;
2478 struct in6_addr gateway;
2479};
2480
2481static struct rt6_info *__ip6_route_redirect(struct net *net,
2482 struct fib6_table *table,
2483 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002484 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002485 int flags)
2486{
2487 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002488 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002489 struct fib6_result res = {};
David Ahern8d1c8022018-04-17 17:33:26 -07002490 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002491 struct fib6_node *fn;
2492
2493 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002494 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002495 *
2496 * RFC 4861 specifies that redirects should only be
2497 * accepted if they come from the nexthop to the target.
2498 * Due to the way the routes are chosen, this notion
2499 * is a bit fuzzy and one might need to check all possible
2500 * routes.
2501 */
2502
Wei Wang66f5d6c2017-10-06 12:06:10 -07002503 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002504 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002505restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002506 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002507 res.f6i = rt;
2508 res.nh = &rt->fib6_nh;
2509
David Ahern14895682018-04-17 17:33:17 -07002510 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002511 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002512 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002513 break;
David Ahern9b6b35a2019-04-16 14:36:02 -07002514 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
David Ahern0b34eb02019-04-09 14:41:19 -07002515 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002516 }
2517
2518 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002519 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002520 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002521 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002522 goto out;
2523 }
2524
David Ahern421842e2018-04-17 17:33:18 -07002525 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002526 fn = fib6_backtrack(fn, &fl6->saddr);
2527 if (fn)
2528 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002529 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002530
David Ahern9b6b35a2019-04-16 14:36:02 -07002531 res.f6i = rt;
2532 res.nh = &rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002533out:
David Ahern23fb93a2018-04-17 17:33:23 -07002534 if (ret)
David Ahern10585b42019-03-20 09:24:50 -07002535 ip6_hold_safe(net, &ret);
David Ahern23fb93a2018-04-17 17:33:23 -07002536 else
David Ahern9b6b35a2019-04-16 14:36:02 -07002537 ret = ip6_create_rt_rcu(&res);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002538
Wei Wang66f5d6c2017-10-06 12:06:10 -07002539 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002540
Paolo Abenib65f1642017-10-19 09:31:43 +02002541 trace_fib6_table_lookup(net, rt, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002542 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002543};
2544
2545static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002546 const struct flowi6 *fl6,
2547 const struct sk_buff *skb,
2548 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002549{
2550 int flags = RT6_LOOKUP_F_HAS_SADDR;
2551 struct ip6rd_flowi rdfl;
2552
2553 rdfl.fl6 = *fl6;
2554 rdfl.gateway = *gateway;
2555
David Ahernb75cc8f2018-03-02 08:32:17 -08002556 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002557 flags, __ip6_route_redirect);
2558}
2559
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002560void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2561 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002562{
2563 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2564 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002565 struct flowi6 fl6 = {
2566 .flowi6_iif = LOOPBACK_IFINDEX,
2567 .flowi6_oif = oif,
2568 .flowi6_mark = mark,
2569 .daddr = iph->daddr,
2570 .saddr = iph->saddr,
2571 .flowlabel = ip6_flowinfo(iph),
2572 .flowi6_uid = uid,
2573 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002574
David Ahernb75cc8f2018-03-02 08:32:17 -08002575 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002576 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002577 dst_release(dst);
2578}
2579EXPORT_SYMBOL_GPL(ip6_redirect);
2580
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002581void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002582{
2583 const struct ipv6hdr *iph = ipv6_hdr(skb);
2584 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2585 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002586 struct flowi6 fl6 = {
2587 .flowi6_iif = LOOPBACK_IFINDEX,
2588 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002589 .daddr = msg->dest,
2590 .saddr = iph->daddr,
2591 .flowi6_uid = sock_net_uid(net, NULL),
2592 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002593
David Ahernb75cc8f2018-03-02 08:32:17 -08002594 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002595 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002596 dst_release(dst);
2597}
2598
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002599void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2600{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002601 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2602 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002603}
2604EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2605
David S. Miller0dbaee32010-12-13 12:52:14 -08002606static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607{
David S. Miller0dbaee32010-12-13 12:52:14 -08002608 struct net_device *dev = dst->dev;
2609 unsigned int mtu = dst_mtu(dst);
2610 struct net *net = dev_net(dev);
2611
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2613
Daniel Lezcano55786892008-03-04 13:47:47 -08002614 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2615 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
2617 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002618 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2619 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2620 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 * rely only on pmtu discovery"
2622 */
2623 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2624 mtu = IPV6_MAXPLEN;
2625 return mtu;
2626}
2627
Steffen Klassertebb762f2011-11-23 02:12:51 +00002628static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002629{
David S. Millerd33e4552010-12-14 13:01:14 -08002630 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002631 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002632
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002633 mtu = dst_metric_raw(dst, RTAX_MTU);
2634 if (mtu)
2635 goto out;
2636
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002637 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002638
2639 rcu_read_lock();
2640 idev = __in6_dev_get(dst->dev);
2641 if (idev)
2642 mtu = idev->cnf.mtu6;
2643 rcu_read_unlock();
2644
Eric Dumazet30f78d82014-04-10 21:23:36 -07002645out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002646 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2647
2648 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002649}
2650
David Ahern901731b2018-05-21 09:08:14 -07002651/* MTU selection:
2652 * 1. mtu on route is locked - use it
2653 * 2. mtu from nexthop exception
2654 * 3. mtu from egress device
2655 *
2656 * based on ip6_dst_mtu_forward and exception logic of
2657 * rt6_find_cached_rt; called with rcu_read_lock
2658 */
David Ahernb748f262019-04-16 14:36:06 -07002659u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2660 const struct in6_addr *daddr,
2661 const struct in6_addr *saddr)
David Ahern901731b2018-05-21 09:08:14 -07002662{
2663 struct rt6_exception_bucket *bucket;
David Ahernb748f262019-04-16 14:36:06 -07002664 const struct fib6_nh *nh = res->nh;
2665 struct fib6_info *f6i = res->f6i;
2666 const struct in6_addr *src_key;
David Ahern901731b2018-05-21 09:08:14 -07002667 struct rt6_exception *rt6_ex;
David Ahern901731b2018-05-21 09:08:14 -07002668 struct inet6_dev *idev;
2669 u32 mtu = 0;
2670
2671 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2672 mtu = f6i->fib6_pmtu;
2673 if (mtu)
2674 goto out;
2675 }
2676
2677 src_key = NULL;
2678#ifdef CONFIG_IPV6_SUBTREES
2679 if (f6i->fib6_src.plen)
2680 src_key = saddr;
2681#endif
2682
2683 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2684 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2685 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2686 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2687
2688 if (likely(!mtu)) {
David Ahernb748f262019-04-16 14:36:06 -07002689 struct net_device *dev = nh->fib_nh_dev;
David Ahern901731b2018-05-21 09:08:14 -07002690
2691 mtu = IPV6_MIN_MTU;
2692 idev = __in6_dev_get(dev);
2693 if (idev && idev->cnf.mtu6 > mtu)
2694 mtu = idev->cnf.mtu6;
2695 }
2696
2697 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2698out:
David Ahernb748f262019-04-16 14:36:06 -07002699 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahern901731b2018-05-21 09:08:14 -07002700}
2701
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002702struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002703 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704{
David S. Miller87a11572011-12-06 17:04:13 -05002705 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 struct rt6_info *rt;
2707 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002708 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709
David S. Miller38308472011-12-03 18:02:47 -05002710 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002711 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712
Martin KaFai Lauad706862015-08-14 11:05:52 -07002713 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002714 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002716 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 goto out;
2718 }
2719
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002720 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002721 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002722 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002723 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002724 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002725 rt->rt6i_dst.plen = 128;
2726 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002727 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728
Ido Schimmel4c981e22018-01-07 12:45:04 +02002729 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002730 * do proper release of the net_device
2731 */
2732 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002733 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734
David S. Miller87a11572011-12-06 17:04:13 -05002735 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2736
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737out:
David S. Miller87a11572011-12-06 17:04:13 -05002738 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739}
2740
Daniel Lezcano569d3642008-01-18 03:56:57 -08002741static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002743 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002744 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2745 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2746 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2747 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2748 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002749 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750
Eric Dumazetfc66f952010-10-08 06:37:34 +00002751 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002752 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002753 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 goto out;
2755
Benjamin Thery6891a342008-03-04 13:49:47 -08002756 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002757 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002758 entries = dst_entries_get_slow(ops);
2759 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002760 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002762 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002763 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764}
2765
David Ahern8c145862016-04-24 21:26:04 -07002766static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2767 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002768 const struct in6_addr *gw_addr,
2769 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002770{
2771 struct flowi6 fl6 = {
2772 .flowi6_oif = cfg->fc_ifindex,
2773 .daddr = *gw_addr,
2774 .saddr = cfg->fc_prefsrc,
2775 };
2776 struct fib6_table *table;
2777 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002778
David Ahernf4797b32018-01-25 16:55:08 -08002779 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002780 if (!table)
2781 return NULL;
2782
2783 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2784 flags |= RT6_LOOKUP_F_HAS_SADDR;
2785
David Ahernf4797b32018-01-25 16:55:08 -08002786 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002787 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002788
2789 /* if table lookup failed, fall back to full lookup */
2790 if (rt == net->ipv6.ip6_null_entry) {
2791 ip6_rt_put(rt);
2792 rt = NULL;
2793 }
2794
2795 return rt;
2796}
2797
David Ahernfc1e64e2018-01-25 16:55:09 -08002798static int ip6_route_check_nh_onlink(struct net *net,
2799 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002800 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002801 struct netlink_ext_ack *extack)
2802{
David Ahern44750f82018-02-06 13:17:06 -08002803 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002804 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2805 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002806 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002807 struct rt6_info *grt;
2808 int err;
2809
2810 err = 0;
2811 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2812 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002813 rcu_read_lock();
2814 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002815 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002816 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002817 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002818 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002819 NL_SET_ERR_MSG(extack,
2820 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002821 err = -EINVAL;
2822 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002823 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002824
2825 ip6_rt_put(grt);
2826 }
2827
2828 return err;
2829}
2830
David Ahern1edce992018-01-25 16:55:07 -08002831static int ip6_route_check_nh(struct net *net,
2832 struct fib6_config *cfg,
2833 struct net_device **_dev,
2834 struct inet6_dev **idev)
2835{
2836 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2837 struct net_device *dev = _dev ? *_dev : NULL;
2838 struct rt6_info *grt = NULL;
2839 int err = -EHOSTUNREACH;
2840
2841 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002842 int flags = RT6_LOOKUP_F_IFACE;
2843
2844 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2845 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002846 if (grt) {
2847 if (grt->rt6i_flags & RTF_GATEWAY ||
2848 (dev && dev != grt->dst.dev)) {
2849 ip6_rt_put(grt);
2850 grt = NULL;
2851 }
2852 }
2853 }
2854
2855 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002856 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002857
2858 if (!grt)
2859 goto out;
2860
2861 if (dev) {
2862 if (dev != grt->dst.dev) {
2863 ip6_rt_put(grt);
2864 goto out;
2865 }
2866 } else {
2867 *_dev = dev = grt->dst.dev;
2868 *idev = grt->rt6i_idev;
2869 dev_hold(dev);
2870 in6_dev_hold(grt->rt6i_idev);
2871 }
2872
2873 if (!(grt->rt6i_flags & RTF_GATEWAY))
2874 err = 0;
2875
2876 ip6_rt_put(grt);
2877
2878out:
2879 return err;
2880}
2881
David Ahern9fbb7042018-03-13 08:29:36 -07002882static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2883 struct net_device **_dev, struct inet6_dev **idev,
2884 struct netlink_ext_ack *extack)
2885{
2886 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2887 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002888 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002889 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002890 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002891 int err = -EINVAL;
2892
2893 /* if gw_addr is local we will fail to detect this in case
2894 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2895 * will return already-added prefix route via interface that
2896 * prefix route was assigned to, which might be non-loopback.
2897 */
David Ahern232378e2018-03-13 08:29:37 -07002898 if (dev &&
2899 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2900 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002901 goto out;
2902 }
2903
2904 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2905 /* IPv6 strictly inhibits using not link-local
2906 * addresses as nexthop address.
2907 * Otherwise, router will not able to send redirects.
2908 * It is very good, but in some (rare!) circumstances
2909 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2910 * some exceptions. --ANK
2911 * We allow IPv4-mapped nexthops to support RFC4798-type
2912 * addressing
2913 */
2914 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2915 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2916 goto out;
2917 }
2918
2919 if (cfg->fc_flags & RTNH_F_ONLINK)
2920 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2921 else
2922 err = ip6_route_check_nh(net, cfg, _dev, idev);
2923
2924 if (err)
2925 goto out;
2926 }
2927
2928 /* reload in case device was changed */
2929 dev = *_dev;
2930
2931 err = -EINVAL;
2932 if (!dev) {
2933 NL_SET_ERR_MSG(extack, "Egress device not specified");
2934 goto out;
2935 } else if (dev->flags & IFF_LOOPBACK) {
2936 NL_SET_ERR_MSG(extack,
2937 "Egress device can not be loopback device for this route");
2938 goto out;
2939 }
David Ahern232378e2018-03-13 08:29:37 -07002940
2941 /* if we did not check gw_addr above, do so now that the
2942 * egress device has been resolved.
2943 */
2944 if (need_addr_check &&
2945 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2946 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2947 goto out;
2948 }
2949
David Ahern9fbb7042018-03-13 08:29:36 -07002950 err = 0;
2951out:
2952 return err;
2953}
2954
David Ahern83c442512019-03-27 20:53:50 -07002955static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2956{
2957 if ((flags & RTF_REJECT) ||
2958 (dev && (dev->flags & IFF_LOOPBACK) &&
2959 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2960 !(flags & RTF_LOCAL)))
2961 return true;
2962
2963 return false;
2964}
2965
2966int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2967 struct fib6_config *cfg, gfp_t gfp_flags,
2968 struct netlink_ext_ack *extack)
2969{
2970 struct net_device *dev = NULL;
2971 struct inet6_dev *idev = NULL;
2972 int addr_type;
2973 int err;
2974
David Ahernf1741732019-03-27 20:53:57 -07002975 fib6_nh->fib_nh_family = AF_INET6;
2976
David Ahern83c442512019-03-27 20:53:50 -07002977 err = -ENODEV;
2978 if (cfg->fc_ifindex) {
2979 dev = dev_get_by_index(net, cfg->fc_ifindex);
2980 if (!dev)
2981 goto out;
2982 idev = in6_dev_get(dev);
2983 if (!idev)
2984 goto out;
2985 }
2986
2987 if (cfg->fc_flags & RTNH_F_ONLINK) {
2988 if (!dev) {
2989 NL_SET_ERR_MSG(extack,
2990 "Nexthop device required for onlink");
2991 goto out;
2992 }
2993
2994 if (!(dev->flags & IFF_UP)) {
2995 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
2996 err = -ENETDOWN;
2997 goto out;
2998 }
2999
David Ahernad1601a2019-03-27 20:53:56 -07003000 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07003001 }
3002
David Ahernad1601a2019-03-27 20:53:56 -07003003 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07003004
3005 /* We cannot add true routes via loopback here,
3006 * they would result in kernel looping; promote them to reject routes
3007 */
3008 addr_type = ipv6_addr_type(&cfg->fc_dst);
3009 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
3010 /* hold loopback dev/idev if we haven't done so. */
3011 if (dev != net->loopback_dev) {
3012 if (dev) {
3013 dev_put(dev);
3014 in6_dev_put(idev);
3015 }
3016 dev = net->loopback_dev;
3017 dev_hold(dev);
3018 idev = in6_dev_get(dev);
3019 if (!idev) {
3020 err = -ENODEV;
3021 goto out;
3022 }
3023 }
3024 goto set_dev;
3025 }
3026
3027 if (cfg->fc_flags & RTF_GATEWAY) {
3028 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3029 if (err)
3030 goto out;
3031
David Ahernad1601a2019-03-27 20:53:56 -07003032 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003033 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003034 }
3035
3036 err = -ENODEV;
3037 if (!dev)
3038 goto out;
3039
3040 if (idev->cnf.disable_ipv6) {
3041 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3042 err = -EACCES;
3043 goto out;
3044 }
3045
3046 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3047 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3048 err = -ENETDOWN;
3049 goto out;
3050 }
3051
3052 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3053 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003054 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003055
David Ahern979e2762019-03-27 20:53:58 -07003056 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3057 cfg->fc_encap_type, cfg, gfp_flags, extack);
3058 if (err)
3059 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003060set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003061 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003062 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003063 err = 0;
3064out:
3065 if (idev)
3066 in6_dev_put(idev);
3067
3068 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003069 lwtstate_put(fib6_nh->fib_nh_lws);
3070 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003071 if (dev)
3072 dev_put(dev);
3073 }
3074
3075 return err;
3076}
3077
David Aherndac7d0f2019-03-27 20:53:51 -07003078void fib6_nh_release(struct fib6_nh *fib6_nh)
3079{
David Ahern979e2762019-03-27 20:53:58 -07003080 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003081}
3082
David Ahern8d1c8022018-04-17 17:33:26 -07003083static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003084 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003085 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086{
Daniel Lezcano55786892008-03-04 13:47:47 -08003087 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003088 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003089 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003090 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003091 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092
David Ahern557c44b2017-04-19 14:19:43 -07003093 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003094 if (cfg->fc_flags & RTF_PCPU) {
3095 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003096 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003097 }
David Ahern557c44b2017-04-19 14:19:43 -07003098
Wei Wang2ea23522017-10-27 17:30:12 -07003099 /* RTF_CACHE is an internal flag; can not be set by userspace */
3100 if (cfg->fc_flags & RTF_CACHE) {
3101 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3102 goto out;
3103 }
3104
David Aherne8478e82018-04-17 17:33:13 -07003105 if (cfg->fc_type > RTN_MAX) {
3106 NL_SET_ERR_MSG(extack, "Invalid route type");
3107 goto out;
3108 }
3109
David Ahernd5d531c2017-05-21 10:12:05 -06003110 if (cfg->fc_dst_len > 128) {
3111 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003112 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003113 }
3114 if (cfg->fc_src_len > 128) {
3115 NL_SET_ERR_MSG(extack, "Invalid source address length");
3116 goto out;
3117 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003118#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003119 if (cfg->fc_src_len) {
3120 NL_SET_ERR_MSG(extack,
3121 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003122 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003123 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003125
Matti Vaittinend71314b2011-11-14 00:14:49 +00003126 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003127 if (cfg->fc_nlinfo.nlh &&
3128 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003129 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003130 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003131 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003132 table = fib6_new_table(net, cfg->fc_table);
3133 }
3134 } else {
3135 table = fib6_new_table(net, cfg->fc_table);
3136 }
David S. Miller38308472011-12-03 18:02:47 -05003137
3138 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003139 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003140
David Ahern93531c62018-04-17 17:33:25 -07003141 err = -ENOMEM;
3142 rt = fib6_info_alloc(gfp_flags);
3143 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003145
David Ahernd7e774f2018-11-06 12:51:15 -08003146 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3147 extack);
David Ahern767a2212018-10-04 20:07:51 -07003148 if (IS_ERR(rt->fib6_metrics)) {
3149 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003150 /* Do not leave garbage there. */
3151 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003152 goto out;
3153 }
3154
David Ahern93531c62018-04-17 17:33:25 -07003155 if (cfg->fc_flags & RTF_ADDRCONF)
3156 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157
Gao feng1716a962012-04-06 00:13:10 +00003158 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003159 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003160 clock_t_to_jiffies(cfg->fc_expires));
3161 else
David Ahern14895682018-04-17 17:33:17 -07003162 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163
Thomas Graf86872cb2006-08-22 00:01:08 -07003164 if (cfg->fc_protocol == RTPROT_UNSPEC)
3165 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003166 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003167
David Ahern83c442512019-03-27 20:53:50 -07003168 rt->fib6_table = table;
3169 rt->fib6_metric = cfg->fc_metric;
3170 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003171 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003172
David Ahern93c2fb22018-04-18 15:38:59 -07003173 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3174 rt->fib6_dst.plen = cfg->fc_dst_len;
3175 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003176 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003177
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003179 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3180 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181#endif
David Ahern83c442512019-03-27 20:53:50 -07003182 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3183 if (err)
3184 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003185
3186 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003187 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 */
David Ahern83c442512019-03-27 20:53:50 -07003189 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003190 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003191 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003192
Daniel Walterc3968a82011-04-13 21:10:57 +00003193 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003194 struct net_device *dev = fib6_info_nh_dev(rt);
3195
Daniel Walterc3968a82011-04-13 21:10:57 +00003196 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003197 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003198 err = -EINVAL;
3199 goto out;
3200 }
David Ahern93c2fb22018-04-18 15:38:59 -07003201 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3202 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003203 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003204 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003205
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003206 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207out:
David Ahern93531c62018-04-17 17:33:25 -07003208 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003209 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003210}
3211
David Ahernacb54e32018-04-17 17:33:22 -07003212int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003213 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003214{
David Ahern8d1c8022018-04-17 17:33:26 -07003215 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003216 int err;
3217
David Ahernacb54e32018-04-17 17:33:22 -07003218 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003219 if (IS_ERR(rt))
3220 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003221
David Ahernd4ead6b2018-04-17 17:33:16 -07003222 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003223 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003224
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225 return err;
3226}
3227
David Ahern8d1c8022018-04-17 17:33:26 -07003228static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003229{
David Ahernafb1d4b52018-04-17 17:33:11 -07003230 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003231 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003232 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003233
David Ahern421842e2018-04-17 17:33:18 -07003234 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003235 err = -ENOENT;
3236 goto out;
3237 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003238
David Ahern93c2fb22018-04-18 15:38:59 -07003239 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003240 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003241 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003242 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243
Gao feng6825a262012-09-19 19:25:34 +00003244out:
David Ahern93531c62018-04-17 17:33:25 -07003245 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 return err;
3247}
3248
David Ahern8d1c8022018-04-17 17:33:26 -07003249int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003250{
David Ahernafb1d4b52018-04-17 17:33:11 -07003251 struct nl_info info = { .nl_net = net };
3252
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003253 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003254}
3255
David Ahern8d1c8022018-04-17 17:33:26 -07003256static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003257{
3258 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003259 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003260 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003261 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003262 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003263
David Ahern421842e2018-04-17 17:33:18 -07003264 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003265 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003266 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003267 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003268
David Ahern93c2fb22018-04-18 15:38:59 -07003269 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003270 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003271
David Ahern16a16cd2017-02-02 12:37:11 -08003272 /* prefer to send a single notification with all hops */
3273 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3274 if (skb) {
3275 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3276
David Ahernd4ead6b2018-04-17 17:33:16 -07003277 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003278 NULL, NULL, 0, RTM_DELROUTE,
3279 info->portid, seq, 0) < 0) {
3280 kfree_skb(skb);
3281 skb = NULL;
3282 } else
3283 info->skip_notify = 1;
3284 }
3285
David Ahern0ae81332017-02-02 12:37:08 -08003286 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003287 &rt->fib6_siblings,
3288 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003289 err = fib6_del(sibling, info);
3290 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003291 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003292 }
3293 }
3294
3295 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003296out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003297 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003298out_put:
David Ahern93531c62018-04-17 17:33:25 -07003299 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003300
3301 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003302 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003303 info->nlh, gfp_any());
3304 }
David Ahern0ae81332017-02-02 12:37:08 -08003305 return err;
3306}
3307
David Ahern23fb93a2018-04-17 17:33:23 -07003308static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3309{
3310 int rc = -ESRCH;
3311
3312 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3313 goto out;
3314
3315 if (cfg->fc_flags & RTF_GATEWAY &&
3316 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3317 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003318
3319 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003320out:
3321 return rc;
3322}
3323
David Ahern333c4302017-05-21 10:12:04 -06003324static int ip6_route_del(struct fib6_config *cfg,
3325 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326{
David Ahern8d1c8022018-04-17 17:33:26 -07003327 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003328 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003329 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003331 int err = -ESRCH;
3332
Daniel Lezcano55786892008-03-04 13:47:47 -08003333 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003334 if (!table) {
3335 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003336 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338
Wei Wang66f5d6c2017-10-06 12:06:10 -07003339 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003340
3341 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003342 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003343 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003344 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003345
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003347 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003348 struct fib6_nh *nh;
3349
Wei Wang2b760fc2017-10-06 12:06:03 -07003350 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003351 struct fib6_result res = {
3352 .f6i = rt,
3353 };
David Ahern23fb93a2018-04-17 17:33:23 -07003354 int rc;
3355
David Ahern7e4b5122019-04-16 14:36:00 -07003356 rt_cache = rt6_find_cached_rt(&res,
3357 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003358 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003359 if (rt_cache) {
3360 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003361 if (rc != -ESRCH) {
3362 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003363 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003364 }
David Ahern23fb93a2018-04-17 17:33:23 -07003365 }
3366 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003367 }
David Ahernad1601a2019-03-27 20:53:56 -07003368
3369 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003370 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003371 (!nh->fib_nh_dev ||
3372 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003373 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003374 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003375 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003376 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003377 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003378 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003379 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003380 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003381 if (!fib6_info_hold_safe(rt))
3382 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003383 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003384
David Ahern0ae81332017-02-02 12:37:08 -08003385 /* if gateway was specified only delete the one hop */
3386 if (cfg->fc_flags & RTF_GATEWAY)
3387 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3388
3389 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390 }
3391 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003392 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003393
3394 return err;
3395}
3396
David S. Miller6700c272012-07-17 03:29:28 -07003397static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003398{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003399 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003400 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003401 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003402 struct ndisc_options ndopts;
3403 struct inet6_dev *in6_dev;
3404 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003405 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003406 int optlen, on_link;
3407 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003408
Simon Horman29a3cad2013-05-28 20:34:26 +00003409 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003410 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003411
3412 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003413 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003414 return;
3415 }
3416
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003417 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003418
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003419 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003420 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003421 return;
3422 }
3423
David S. Miller6e157b62012-07-12 00:05:02 -07003424 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003425 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003426 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003427 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003428 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003429 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003430 return;
3431 }
3432
3433 in6_dev = __in6_dev_get(skb->dev);
3434 if (!in6_dev)
3435 return;
3436 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3437 return;
3438
3439 /* RFC2461 8.1:
3440 * The IP source address of the Redirect MUST be the same as the current
3441 * first-hop router for the specified ICMP Destination Address.
3442 */
3443
Alexander Aringf997c552016-06-15 21:20:23 +02003444 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003445 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3446 return;
3447 }
David S. Miller6e157b62012-07-12 00:05:02 -07003448
3449 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003450 if (ndopts.nd_opts_tgt_lladdr) {
3451 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3452 skb->dev);
3453 if (!lladdr) {
3454 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3455 return;
3456 }
3457 }
3458
David S. Miller6e157b62012-07-12 00:05:02 -07003459 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003460 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003461 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3462 return;
3463 }
3464
3465 /* Redirect received -> path was valid.
3466 * Look, redirects are sent only in response to data packets,
3467 * so that this nexthop apparently is reachable. --ANK
3468 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003469 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003470
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003471 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003472 if (!neigh)
3473 return;
3474
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475 /*
3476 * We have finally decided to accept it.
3477 */
3478
Alexander Aringf997c552016-06-15 21:20:23 +02003479 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3481 NEIGH_UPDATE_F_OVERRIDE|
3482 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003483 NEIGH_UPDATE_F_ISROUTER)),
3484 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485
David Ahern4d85cd02018-04-20 15:37:59 -07003486 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003487 res.f6i = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003488 /* This fib6_info_hold() is safe here because we hold reference to rt
3489 * and rt already holds reference to fib6_info.
3490 */
David Ahern85bd05d2019-04-16 14:36:01 -07003491 fib6_info_hold(res.f6i);
David Ahern4d85cd02018-04-20 15:37:59 -07003492 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003493
David Ahern85bd05d2019-04-16 14:36:01 -07003494 res.nh = &res.f6i->fib6_nh;
3495 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003496 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003497 goto out;
3498
3499 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3500 if (on_link)
3501 nrt->rt6i_flags &= ~RTF_GATEWAY;
3502
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003503 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504
Wei Wang2b760fc2017-10-06 12:06:03 -07003505 /* No need to remove rt from the exception table if rt is
3506 * a cached route because rt6_insert_exception() will
3507 * takes care of it
3508 */
David Ahern5012f0a2019-04-16 14:36:05 -07003509 if (rt6_insert_exception(nrt, &res)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003510 dst_release_immediate(&nrt->dst);
3511 goto out;
3512 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513
Changli Gaod8d1f302010-06-10 23:31:35 -07003514 netevent.old = &rt->dst;
3515 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003516 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003517 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003518 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3519
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520out:
David Ahern85bd05d2019-04-16 14:36:01 -07003521 fib6_info_release(res.f6i);
David S. Millere8599ff2012-07-11 23:43:53 -07003522 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003523}
3524
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003525#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003526static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003527 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003528 const struct in6_addr *gwaddr,
3529 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003530{
David Ahern830218c2016-10-24 10:52:35 -07003531 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3532 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003533 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003534 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003535 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003536
David Ahern830218c2016-10-24 10:52:35 -07003537 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003538 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003539 return NULL;
3540
Wei Wang66f5d6c2017-10-06 12:06:10 -07003541 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003542 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003543 if (!fn)
3544 goto out;
3545
Wei Wang66f5d6c2017-10-06 12:06:10 -07003546 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003547 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003548 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003549 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003550 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003551 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003552 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003553 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003554 if (!fib6_info_hold_safe(rt))
3555 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003556 break;
3557 }
3558out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003559 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003560 return rt;
3561}
3562
David Ahern8d1c8022018-04-17 17:33:26 -07003563static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003564 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003565 const struct in6_addr *gwaddr,
3566 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003567 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003568{
Thomas Graf86872cb2006-08-22 00:01:08 -07003569 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003570 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003571 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003572 .fc_dst_len = prefixlen,
3573 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3574 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003575 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003576 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003577 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003578 .fc_nlinfo.nlh = NULL,
3579 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003580 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003581
David Ahern830218c2016-10-24 10:52:35 -07003582 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003583 cfg.fc_dst = *prefix;
3584 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003585
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003586 /* We should treat it as a default route if prefix length is 0. */
3587 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003588 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003589
David Ahernacb54e32018-04-17 17:33:22 -07003590 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003591
David Ahern830218c2016-10-24 10:52:35 -07003592 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003593}
3594#endif
3595
David Ahern8d1c8022018-04-17 17:33:26 -07003596struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003597 const struct in6_addr *addr,
3598 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003599{
David Ahern830218c2016-10-24 10:52:35 -07003600 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003601 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003602 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603
David Ahernafb1d4b52018-04-17 17:33:11 -07003604 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003605 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003606 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003607
Wei Wang66f5d6c2017-10-06 12:06:10 -07003608 rcu_read_lock();
3609 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003610 struct fib6_nh *nh = &rt->fib6_nh;
3611
3612 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003613 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003614 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615 break;
3616 }
Wei Wange873e4b2018-07-21 20:56:32 -07003617 if (rt && !fib6_info_hold_safe(rt))
3618 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003619 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620 return rt;
3621}
3622
David Ahern8d1c8022018-04-17 17:33:26 -07003623struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003624 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003625 struct net_device *dev,
3626 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627{
Thomas Graf86872cb2006-08-22 00:01:08 -07003628 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003629 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003630 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003631 .fc_ifindex = dev->ifindex,
3632 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3633 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003634 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003635 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003636 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003637 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003638 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003639 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003641 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003642
David Ahernacb54e32018-04-17 17:33:22 -07003643 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003644 struct fib6_table *table;
3645
3646 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3647 if (table)
3648 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3649 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650
David Ahernafb1d4b52018-04-17 17:33:11 -07003651 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652}
3653
David Ahernafb1d4b52018-04-17 17:33:11 -07003654static void __rt6_purge_dflt_routers(struct net *net,
3655 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656{
David Ahern8d1c8022018-04-17 17:33:26 -07003657 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003658
3659restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003660 rcu_read_lock();
3661 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003662 struct net_device *dev = fib6_info_nh_dev(rt);
3663 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3664
David Ahern93c2fb22018-04-18 15:38:59 -07003665 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003666 (!idev || idev->cnf.accept_ra != 2) &&
3667 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003668 rcu_read_unlock();
3669 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003670 goto restart;
3671 }
3672 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003673 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003674
3675 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3676}
3677
3678void rt6_purge_dflt_routers(struct net *net)
3679{
3680 struct fib6_table *table;
3681 struct hlist_head *head;
3682 unsigned int h;
3683
3684 rcu_read_lock();
3685
3686 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3687 head = &net->ipv6.fib_table_hash[h];
3688 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3689 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003690 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003691 }
3692 }
3693
3694 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695}
3696
Daniel Lezcano55786892008-03-04 13:47:47 -08003697static void rtmsg_to_fib6_config(struct net *net,
3698 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003699 struct fib6_config *cfg)
3700{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003701 *cfg = (struct fib6_config){
3702 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3703 : RT6_TABLE_MAIN,
3704 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003705 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003706 .fc_expires = rtmsg->rtmsg_info,
3707 .fc_dst_len = rtmsg->rtmsg_dst_len,
3708 .fc_src_len = rtmsg->rtmsg_src_len,
3709 .fc_flags = rtmsg->rtmsg_flags,
3710 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003711
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003712 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003713
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003714 .fc_dst = rtmsg->rtmsg_dst,
3715 .fc_src = rtmsg->rtmsg_src,
3716 .fc_gateway = rtmsg->rtmsg_gateway,
3717 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003718}
3719
Daniel Lezcano55786892008-03-04 13:47:47 -08003720int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721{
Thomas Graf86872cb2006-08-22 00:01:08 -07003722 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723 struct in6_rtmsg rtmsg;
3724 int err;
3725
Ian Morris67ba4152014-08-24 21:53:10 +01003726 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727 case SIOCADDRT: /* Add a route */
3728 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003729 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730 return -EPERM;
3731 err = copy_from_user(&rtmsg, arg,
3732 sizeof(struct in6_rtmsg));
3733 if (err)
3734 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003735
Daniel Lezcano55786892008-03-04 13:47:47 -08003736 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003737
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738 rtnl_lock();
3739 switch (cmd) {
3740 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003741 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742 break;
3743 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003744 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 break;
3746 default:
3747 err = -EINVAL;
3748 }
3749 rtnl_unlock();
3750
3751 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003752 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003753
3754 return -EINVAL;
3755}
3756
3757/*
3758 * Drop the packet on the floor
3759 */
3760
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003761static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003763 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003764 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003765 switch (ipstats_mib_noroutes) {
3766 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003767 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003768 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003769 IP6_INC_STATS(dev_net(dst->dev),
3770 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003771 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003772 break;
3773 }
3774 /* FALLTHROUGH */
3775 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003776 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3777 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003778 break;
3779 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003780 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781 kfree_skb(skb);
3782 return 0;
3783}
3784
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003785static int ip6_pkt_discard(struct sk_buff *skb)
3786{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003787 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003788}
3789
Eric W. Biedermanede20592015-10-07 16:48:47 -05003790static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791{
Eric Dumazetadf30902009-06-02 05:19:30 +00003792 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003793 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794}
3795
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003796static int ip6_pkt_prohibit(struct sk_buff *skb)
3797{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003798 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003799}
3800
Eric W. Biedermanede20592015-10-07 16:48:47 -05003801static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003802{
Eric Dumazetadf30902009-06-02 05:19:30 +00003803 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003804 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003805}
3806
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807/*
3808 * Allocate a dst for local (unicast / anycast) address.
3809 */
3810
David Ahern360a9882018-04-18 15:39:00 -07003811struct fib6_info *addrconf_f6i_alloc(struct net *net,
3812 struct inet6_dev *idev,
3813 const struct in6_addr *addr,
3814 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003815{
David Ahernc7a1ce32019-03-21 05:21:35 -07003816 struct fib6_config cfg = {
3817 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3818 .fc_ifindex = idev->dev->ifindex,
3819 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3820 .fc_dst = *addr,
3821 .fc_dst_len = 128,
3822 .fc_protocol = RTPROT_KERNEL,
3823 .fc_nlinfo.nl_net = net,
3824 .fc_ignore_dev_down = true,
3825 };
David Ahern5f02ce242016-09-10 12:09:54 -07003826
David Aherne8478e82018-04-17 17:33:13 -07003827 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003828 cfg.fc_type = RTN_ANYCAST;
3829 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003830 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003831 cfg.fc_type = RTN_LOCAL;
3832 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003833 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834
David Ahernc7a1ce32019-03-21 05:21:35 -07003835 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836}
3837
Daniel Walterc3968a82011-04-13 21:10:57 +00003838/* remove deleted ip from prefsrc entries */
3839struct arg_dev_net_ip {
3840 struct net_device *dev;
3841 struct net *net;
3842 struct in6_addr *addr;
3843};
3844
David Ahern8d1c8022018-04-17 17:33:26 -07003845static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003846{
3847 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3848 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3849 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3850
David Ahernad1601a2019-03-27 20:53:56 -07003851 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003852 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003853 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003854 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003855 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003856 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003857 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003858 }
3859 return 0;
3860}
3861
3862void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3863{
3864 struct net *net = dev_net(ifp->idev->dev);
3865 struct arg_dev_net_ip adni = {
3866 .dev = ifp->idev->dev,
3867 .net = net,
3868 .addr = &ifp->addr,
3869 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003870 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003871}
3872
David Ahern2b2450c2019-03-27 20:53:52 -07003873#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003874
3875/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003876static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003877{
3878 struct in6_addr *gateway = (struct in6_addr *)arg;
3879
David Ahern93c2fb22018-04-18 15:38:59 -07003880 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003881 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003882 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003883 return -1;
3884 }
Wei Wangb16cb452017-10-06 12:06:00 -07003885
3886 /* Further clean up cached routes in exception table.
3887 * This is needed because cached route may have a different
3888 * gateway than its 'parent' in the case of an ip redirect.
3889 */
3890 rt6_exceptions_clean_tohost(rt, gateway);
3891
Duan Jiongbe7a0102014-05-15 15:56:14 +08003892 return 0;
3893}
3894
3895void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3896{
3897 fib6_clean_all(net, fib6_clean_tohost, gateway);
3898}
3899
Ido Schimmel2127d952018-01-07 12:45:03 +02003900struct arg_netdev_event {
3901 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003902 union {
3903 unsigned int nh_flags;
3904 unsigned long event;
3905 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003906};
3907
David Ahern8d1c8022018-04-17 17:33:26 -07003908static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003909{
David Ahern8d1c8022018-04-17 17:33:26 -07003910 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003911 struct fib6_node *fn;
3912
David Ahern93c2fb22018-04-18 15:38:59 -07003913 fn = rcu_dereference_protected(rt->fib6_node,
3914 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003915 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003916 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003917 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003918 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003919 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003920 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003921 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003922 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003923 }
3924
3925 return NULL;
3926}
3927
David Ahern8d1c8022018-04-17 17:33:26 -07003928static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003929{
David Ahernad1601a2019-03-27 20:53:56 -07003930 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3931 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3932 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003933 return true;
3934
3935 return false;
3936}
3937
David Ahern8d1c8022018-04-17 17:33:26 -07003938static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003939{
David Ahern8d1c8022018-04-17 17:33:26 -07003940 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003941 int total = 0;
3942
3943 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003944 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003945
David Ahern93c2fb22018-04-18 15:38:59 -07003946 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003947 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003948 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003949 }
3950
3951 return total;
3952}
3953
David Ahern8d1c8022018-04-17 17:33:26 -07003954static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003955{
3956 int upper_bound = -1;
3957
3958 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003959 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003960 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3961 total) - 1;
3962 }
David Ahernad1601a2019-03-27 20:53:56 -07003963 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003964}
3965
David Ahern8d1c8022018-04-17 17:33:26 -07003966static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003967{
David Ahern8d1c8022018-04-17 17:33:26 -07003968 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003969 int weight = 0;
3970
3971 rt6_upper_bound_set(rt, &weight, total);
3972
David Ahern93c2fb22018-04-18 15:38:59 -07003973 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003974 rt6_upper_bound_set(iter, &weight, total);
3975}
3976
David Ahern8d1c8022018-04-17 17:33:26 -07003977void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003978{
David Ahern8d1c8022018-04-17 17:33:26 -07003979 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003980 int total;
3981
3982 /* In case the entire multipath route was marked for flushing,
3983 * then there is no need to rebalance upon the removal of every
3984 * sibling route.
3985 */
David Ahern93c2fb22018-04-18 15:38:59 -07003986 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003987 return;
3988
3989 /* During lookup routes are evaluated in order, so we need to
3990 * make sure upper bounds are assigned from the first sibling
3991 * onwards.
3992 */
3993 first = rt6_multipath_first_sibling(rt);
3994 if (WARN_ON_ONCE(!first))
3995 return;
3996
3997 total = rt6_multipath_total_weight(first);
3998 rt6_multipath_upper_bound_set(first, total);
3999}
4000
David Ahern8d1c8022018-04-17 17:33:26 -07004001static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02004002{
4003 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07004004 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02004005
David Ahernad1601a2019-03-27 20:53:56 -07004006 if (rt != net->ipv6.fib6_null_entry &&
4007 rt->fib6_nh.fib_nh_dev == arg->dev) {
4008 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07004009 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004010 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004011 }
Ido Schimmel2127d952018-01-07 12:45:03 +02004012
4013 return 0;
4014}
4015
4016void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
4017{
4018 struct arg_netdev_event arg = {
4019 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004020 {
4021 .nh_flags = nh_flags,
4022 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004023 };
4024
4025 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4026 arg.nh_flags |= RTNH_F_LINKDOWN;
4027
4028 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4029}
4030
David Ahern8d1c8022018-04-17 17:33:26 -07004031static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004032 const struct net_device *dev)
4033{
David Ahern8d1c8022018-04-17 17:33:26 -07004034 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004035
David Ahernad1601a2019-03-27 20:53:56 -07004036 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004037 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004038 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004039 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004040 return true;
4041
4042 return false;
4043}
4044
David Ahern8d1c8022018-04-17 17:33:26 -07004045static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004046{
David Ahern8d1c8022018-04-17 17:33:26 -07004047 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004048
4049 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004050 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004051 iter->should_flush = 1;
4052}
4053
David Ahern8d1c8022018-04-17 17:33:26 -07004054static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004055 const struct net_device *down_dev)
4056{
David Ahern8d1c8022018-04-17 17:33:26 -07004057 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004058 unsigned int dead = 0;
4059
David Ahernad1601a2019-03-27 20:53:56 -07004060 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4061 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004062 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004063 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004064 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4065 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004066 dead++;
4067
4068 return dead;
4069}
4070
David Ahern8d1c8022018-04-17 17:33:26 -07004071static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004072 const struct net_device *dev,
4073 unsigned int nh_flags)
4074{
David Ahern8d1c8022018-04-17 17:33:26 -07004075 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004076
David Ahernad1601a2019-03-27 20:53:56 -07004077 if (rt->fib6_nh.fib_nh_dev == dev)
4078 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004079 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004080 if (iter->fib6_nh.fib_nh_dev == dev)
4081 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004082}
4083
David Aherna1a22c12017-01-18 07:40:36 -08004084/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004085static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004086{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004087 const struct arg_netdev_event *arg = p_arg;
4088 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004089 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004090
David Ahern421842e2018-04-17 17:33:18 -07004091 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004092 return 0;
4093
4094 switch (arg->event) {
4095 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004096 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004097 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004098 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004099 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004100 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004101 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004102 if (rt6_multipath_uses_dev(rt, dev)) {
4103 unsigned int count;
4104
4105 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004106 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004107 rt6_multipath_flush(rt);
4108 return -1;
4109 }
4110 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4111 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004112 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004113 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004114 }
4115 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004116 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004117 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004118 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004119 break;
David Ahernad1601a2019-03-27 20:53:56 -07004120 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004121 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004122 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004123 }
David S. Millerc159d302011-12-26 15:24:36 -05004124
Linus Torvalds1da177e2005-04-16 15:20:36 -07004125 return 0;
4126}
4127
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004128void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004129{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004130 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004131 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004132 {
4133 .event = event,
4134 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004135 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004136 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004137
David Ahern7c6bb7d2018-10-11 20:17:21 -07004138 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4139 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4140 else
4141 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004142}
4143
4144void rt6_disable_ip(struct net_device *dev, unsigned long event)
4145{
4146 rt6_sync_down_dev(dev, event);
4147 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4148 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004149}
4150
Eric Dumazet95c96172012-04-15 05:58:06 +00004151struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004152 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004153 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004154};
4155
David Ahern8d1c8022018-04-17 17:33:26 -07004156static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004157{
4158 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4159 struct inet6_dev *idev;
4160
4161 /* In IPv6 pmtu discovery is not optional,
4162 so that RTAX_MTU lock cannot disable it.
4163 We still use this lock to block changes
4164 caused by addrconf/ndisc.
4165 */
4166
4167 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004168 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004169 return 0;
4170
4171 /* For administrative MTU increase, there is no way to discover
4172 IPv6 PMTU increase, so PMTU increase should be updated here.
4173 Since RFC 1981 doesn't include administrative MTU increase
4174 update PMTU increase is a MUST. (i.e. jumbo frame)
4175 */
David Ahernad1601a2019-03-27 20:53:56 -07004176 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004177 !fib6_metric_locked(rt, RTAX_MTU)) {
4178 u32 mtu = rt->fib6_pmtu;
4179
4180 if (mtu >= arg->mtu ||
4181 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4182 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4183
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004184 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004185 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004186 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004188 return 0;
4189}
4190
Eric Dumazet95c96172012-04-15 05:58:06 +00004191void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192{
Thomas Grafc71099a2006-08-04 23:20:06 -07004193 struct rt6_mtu_change_arg arg = {
4194 .dev = dev,
4195 .mtu = mtu,
4196 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004197
Li RongQing0c3584d2013-12-27 16:32:38 +08004198 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004199}
4200
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004201static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004202 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004203 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004204 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004205 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004206 [RTA_PRIORITY] = { .type = NLA_U32 },
4207 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004208 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004209 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004210 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4211 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004212 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004213 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004214 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004215 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004216 [RTA_IP_PROTO] = { .type = NLA_U8 },
4217 [RTA_SPORT] = { .type = NLA_U16 },
4218 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004219};
4220
4221static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004222 struct fib6_config *cfg,
4223 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004224{
Thomas Graf86872cb2006-08-22 00:01:08 -07004225 struct rtmsg *rtm;
4226 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004227 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004228 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004229
Johannes Bergfceb6432017-04-12 14:34:07 +02004230 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004231 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004232 if (err < 0)
4233 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004234
Thomas Graf86872cb2006-08-22 00:01:08 -07004235 err = -EINVAL;
4236 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004237
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004238 *cfg = (struct fib6_config){
4239 .fc_table = rtm->rtm_table,
4240 .fc_dst_len = rtm->rtm_dst_len,
4241 .fc_src_len = rtm->rtm_src_len,
4242 .fc_flags = RTF_UP,
4243 .fc_protocol = rtm->rtm_protocol,
4244 .fc_type = rtm->rtm_type,
4245
4246 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4247 .fc_nlinfo.nlh = nlh,
4248 .fc_nlinfo.nl_net = sock_net(skb->sk),
4249 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004250
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004251 if (rtm->rtm_type == RTN_UNREACHABLE ||
4252 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004253 rtm->rtm_type == RTN_PROHIBIT ||
4254 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004255 cfg->fc_flags |= RTF_REJECT;
4256
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004257 if (rtm->rtm_type == RTN_LOCAL)
4258 cfg->fc_flags |= RTF_LOCAL;
4259
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004260 if (rtm->rtm_flags & RTM_F_CLONED)
4261 cfg->fc_flags |= RTF_CACHE;
4262
David Ahernfc1e64e2018-01-25 16:55:09 -08004263 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4264
Thomas Graf86872cb2006-08-22 00:01:08 -07004265 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004266 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004267 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268 }
David Aherne3818542019-02-26 09:00:03 -08004269 if (tb[RTA_VIA]) {
4270 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4271 goto errout;
4272 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004273
4274 if (tb[RTA_DST]) {
4275 int plen = (rtm->rtm_dst_len + 7) >> 3;
4276
4277 if (nla_len(tb[RTA_DST]) < plen)
4278 goto errout;
4279
4280 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004281 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004282
4283 if (tb[RTA_SRC]) {
4284 int plen = (rtm->rtm_src_len + 7) >> 3;
4285
4286 if (nla_len(tb[RTA_SRC]) < plen)
4287 goto errout;
4288
4289 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004290 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004291
Daniel Walterc3968a82011-04-13 21:10:57 +00004292 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004293 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004294
Thomas Graf86872cb2006-08-22 00:01:08 -07004295 if (tb[RTA_OIF])
4296 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4297
4298 if (tb[RTA_PRIORITY])
4299 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4300
4301 if (tb[RTA_METRICS]) {
4302 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4303 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004304 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004305
4306 if (tb[RTA_TABLE])
4307 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4308
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004309 if (tb[RTA_MULTIPATH]) {
4310 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4311 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004312
4313 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004314 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004315 if (err < 0)
4316 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004317 }
4318
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004319 if (tb[RTA_PREF]) {
4320 pref = nla_get_u8(tb[RTA_PREF]);
4321 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4322 pref != ICMPV6_ROUTER_PREF_HIGH)
4323 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4324 cfg->fc_flags |= RTF_PREF(pref);
4325 }
4326
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004327 if (tb[RTA_ENCAP])
4328 cfg->fc_encap = tb[RTA_ENCAP];
4329
David Ahern9ed59592017-01-17 14:57:36 -08004330 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004331 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4332
David Ahernc255bd62017-05-27 16:19:27 -06004333 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004334 if (err < 0)
4335 goto errout;
4336 }
4337
Xin Long32bc2012015-12-16 17:50:11 +08004338 if (tb[RTA_EXPIRES]) {
4339 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4340
4341 if (addrconf_finite_timeout(timeout)) {
4342 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4343 cfg->fc_flags |= RTF_EXPIRES;
4344 }
4345 }
4346
Thomas Graf86872cb2006-08-22 00:01:08 -07004347 err = 0;
4348errout:
4349 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004350}
4351
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004352struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004353 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004354 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004355 struct list_head next;
4356};
4357
David Ahernd4ead6b2018-04-17 17:33:16 -07004358static int ip6_route_info_append(struct net *net,
4359 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004360 struct fib6_info *rt,
4361 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004362{
4363 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004364 int err = -EEXIST;
4365
4366 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004367 /* check if fib6_info already exists */
4368 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004369 return err;
4370 }
4371
4372 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4373 if (!nh)
4374 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004375 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004376 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4377 list_add_tail(&nh->next, rt6_nh_list);
4378
4379 return 0;
4380}
4381
David Ahern8d1c8022018-04-17 17:33:26 -07004382static void ip6_route_mpath_notify(struct fib6_info *rt,
4383 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004384 struct nl_info *info,
4385 __u16 nlflags)
4386{
4387 /* if this is an APPEND route, then rt points to the first route
4388 * inserted and rt_last points to last route inserted. Userspace
4389 * wants a consistent dump of the route which starts at the first
4390 * nexthop. Since sibling routes are always added at the end of
4391 * the list, find the first sibling of the last route appended
4392 */
David Ahern93c2fb22018-04-18 15:38:59 -07004393 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4394 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004395 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004396 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004397 }
4398
4399 if (rt)
4400 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4401}
4402
David Ahern333c4302017-05-21 10:12:04 -06004403static int ip6_route_multipath_add(struct fib6_config *cfg,
4404 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004405{
David Ahern8d1c8022018-04-17 17:33:26 -07004406 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004407 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004408 struct fib6_config r_cfg;
4409 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004410 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004411 struct rt6_nh *err_nh;
4412 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004413 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004414 int remaining;
4415 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004416 int err = 1;
4417 int nhn = 0;
4418 int replace = (cfg->fc_nlinfo.nlh &&
4419 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4420 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004421
David Ahern3b1137f2017-02-02 12:37:10 -08004422 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4423 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4424 nlflags |= NLM_F_APPEND;
4425
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004426 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004427 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004428
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004429 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004430 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004431 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004432 while (rtnh_ok(rtnh, remaining)) {
4433 memcpy(&r_cfg, cfg, sizeof(*cfg));
4434 if (rtnh->rtnh_ifindex)
4435 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4436
4437 attrlen = rtnh_attrlen(rtnh);
4438 if (attrlen > 0) {
4439 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4440
4441 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4442 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004443 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004444 r_cfg.fc_flags |= RTF_GATEWAY;
4445 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004446 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4447 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4448 if (nla)
4449 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004450 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004451
David Ahern68e2ffd2018-03-20 10:06:59 -07004452 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004453 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004454 if (IS_ERR(rt)) {
4455 err = PTR_ERR(rt);
4456 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004457 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004458 }
David Ahernb5d2d752018-07-15 09:35:19 -07004459 if (!rt6_qualify_for_ecmp(rt)) {
4460 err = -EINVAL;
4461 NL_SET_ERR_MSG(extack,
4462 "Device only routes can not be added for IPv6 using the multipath API.");
4463 fib6_info_release(rt);
4464 goto cleanup;
4465 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004466
David Ahernad1601a2019-03-27 20:53:56 -07004467 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004468
David Ahernd4ead6b2018-04-17 17:33:16 -07004469 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4470 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004471 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004472 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004473 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004474 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004475
4476 rtnh = rtnh_next(rtnh, &remaining);
4477 }
4478
David Ahern3b1137f2017-02-02 12:37:10 -08004479 /* for add and replace send one notification with all nexthops.
4480 * Skip the notification in fib6_add_rt2node and send one with
4481 * the full route when done
4482 */
4483 info->skip_notify = 1;
4484
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004485 err_nh = NULL;
4486 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004487 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4488 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004489
David Ahernf7225172018-06-04 13:41:42 -07004490 if (!err) {
4491 /* save reference to last route successfully inserted */
4492 rt_last = nh->fib6_info;
4493
4494 /* save reference to first route for notification */
4495 if (!rt_notif)
4496 rt_notif = nh->fib6_info;
4497 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004498
David Ahern8d1c8022018-04-17 17:33:26 -07004499 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4500 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004501 if (err) {
4502 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004503 NL_SET_ERR_MSG_MOD(extack,
4504 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004505 err_nh = nh;
4506 goto add_errout;
4507 }
4508
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004509 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004510 * these flags after the first nexthop: if there is a collision,
4511 * we have already failed to add the first nexthop:
4512 * fib6_add_rt2node() has rejected it; when replacing, old
4513 * nexthops have been replaced by first new, the rest should
4514 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004515 */
Michal Kubeček27596472015-05-18 20:54:00 +02004516 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4517 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004518 nhn++;
4519 }
4520
David Ahern3b1137f2017-02-02 12:37:10 -08004521 /* success ... tell user about new route */
4522 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004523 goto cleanup;
4524
4525add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004526 /* send notification for routes that were added so that
4527 * the delete notifications sent by ip6_route_del are
4528 * coherent
4529 */
4530 if (rt_notif)
4531 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4532
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004533 /* Delete routes that were already added */
4534 list_for_each_entry(nh, &rt6_nh_list, next) {
4535 if (err_nh == nh)
4536 break;
David Ahern333c4302017-05-21 10:12:04 -06004537 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004538 }
4539
4540cleanup:
4541 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004542 if (nh->fib6_info)
4543 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004544 list_del(&nh->next);
4545 kfree(nh);
4546 }
4547
4548 return err;
4549}
4550
David Ahern333c4302017-05-21 10:12:04 -06004551static int ip6_route_multipath_del(struct fib6_config *cfg,
4552 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004553{
4554 struct fib6_config r_cfg;
4555 struct rtnexthop *rtnh;
4556 int remaining;
4557 int attrlen;
4558 int err = 1, last_err = 0;
4559
4560 remaining = cfg->fc_mp_len;
4561 rtnh = (struct rtnexthop *)cfg->fc_mp;
4562
4563 /* Parse a Multipath Entry */
4564 while (rtnh_ok(rtnh, remaining)) {
4565 memcpy(&r_cfg, cfg, sizeof(*cfg));
4566 if (rtnh->rtnh_ifindex)
4567 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4568
4569 attrlen = rtnh_attrlen(rtnh);
4570 if (attrlen > 0) {
4571 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4572
4573 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4574 if (nla) {
4575 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4576 r_cfg.fc_flags |= RTF_GATEWAY;
4577 }
4578 }
David Ahern333c4302017-05-21 10:12:04 -06004579 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004580 if (err)
4581 last_err = err;
4582
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004583 rtnh = rtnh_next(rtnh, &remaining);
4584 }
4585
4586 return last_err;
4587}
4588
David Ahernc21ef3e2017-04-16 09:48:24 -07004589static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4590 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004591{
Thomas Graf86872cb2006-08-22 00:01:08 -07004592 struct fib6_config cfg;
4593 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004594
David Ahern333c4302017-05-21 10:12:04 -06004595 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004596 if (err < 0)
4597 return err;
4598
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004599 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004600 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004601 else {
4602 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004603 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004604 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004605}
4606
David Ahernc21ef3e2017-04-16 09:48:24 -07004607static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4608 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004609{
Thomas Graf86872cb2006-08-22 00:01:08 -07004610 struct fib6_config cfg;
4611 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004612
David Ahern333c4302017-05-21 10:12:04 -06004613 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004614 if (err < 0)
4615 return err;
4616
David Ahern67f69512019-03-21 05:21:34 -07004617 if (cfg.fc_metric == 0)
4618 cfg.fc_metric = IP6_RT_PRIO_USER;
4619
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004620 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004621 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004622 else
David Ahernacb54e32018-04-17 17:33:22 -07004623 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004624}
4625
David Ahern8d1c8022018-04-17 17:33:26 -07004626static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004627{
David Ahernbeb1afac52017-02-02 12:37:09 -08004628 int nexthop_len = 0;
4629
David Ahern93c2fb22018-04-18 15:38:59 -07004630 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004631 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4632 + NLA_ALIGN(sizeof(struct rtnexthop))
4633 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004634 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004635
David Ahern93c2fb22018-04-18 15:38:59 -07004636 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004637 }
4638
Thomas Graf339bf982006-11-10 14:10:15 -08004639 return NLMSG_ALIGN(sizeof(struct rtmsg))
4640 + nla_total_size(16) /* RTA_SRC */
4641 + nla_total_size(16) /* RTA_DST */
4642 + nla_total_size(16) /* RTA_GATEWAY */
4643 + nla_total_size(16) /* RTA_PREFSRC */
4644 + nla_total_size(4) /* RTA_TABLE */
4645 + nla_total_size(4) /* RTA_IIF */
4646 + nla_total_size(4) /* RTA_OIF */
4647 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004648 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004649 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004650 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004651 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004652 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004653 + nexthop_len;
4654}
4655
David Ahernd4ead6b2018-04-17 17:33:16 -07004656static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004657 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004658 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004659 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004660 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004661{
Xin Long22d0bd82018-09-11 14:33:58 +08004662 struct rt6_info *rt6 = (struct rt6_info *)dst;
4663 struct rt6key *rt6_dst, *rt6_src;
4664 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004665 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004666 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004667 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668
Eric W. Biederman15e47302012-09-07 20:12:54 +00004669 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004670 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004671 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004672
Xin Long22d0bd82018-09-11 14:33:58 +08004673 if (rt6) {
4674 rt6_dst = &rt6->rt6i_dst;
4675 rt6_src = &rt6->rt6i_src;
4676 rt6_flags = rt6->rt6i_flags;
4677 } else {
4678 rt6_dst = &rt->fib6_dst;
4679 rt6_src = &rt->fib6_src;
4680 rt6_flags = rt->fib6_flags;
4681 }
4682
Thomas Graf2d7202b2006-08-22 00:01:27 -07004683 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004684 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004685 rtm->rtm_dst_len = rt6_dst->plen;
4686 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004687 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004688 if (rt->fib6_table)
4689 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004690 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004691 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004692 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004693 if (nla_put_u32(skb, RTA_TABLE, table))
4694 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004695
4696 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004697 rtm->rtm_flags = 0;
4698 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004699 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004700
Xin Long22d0bd82018-09-11 14:33:58 +08004701 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004702 rtm->rtm_flags |= RTM_F_CLONED;
4703
David Ahernd4ead6b2018-04-17 17:33:16 -07004704 if (dest) {
4705 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004706 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004707 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004708 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004709 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004710 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711#ifdef CONFIG_IPV6_SUBTREES
4712 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004713 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004714 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004715 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004716 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004717 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004718 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004720 if (iif) {
4721#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004722 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004723 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004724
David Ahernfd61c6b2017-01-17 15:51:07 -08004725 if (err == 0)
4726 return 0;
4727 if (err < 0)
4728 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004729 } else
4730#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004731 if (nla_put_u32(skb, RTA_IIF, iif))
4732 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004733 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004735 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004736 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004737 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004739
David Ahern93c2fb22018-04-18 15:38:59 -07004740 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004741 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004742 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004743 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004744 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004745 }
4746
David Ahernd4ead6b2018-04-17 17:33:16 -07004747 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4748 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004749 goto nla_put_failure;
4750
David Ahern93c2fb22018-04-18 15:38:59 -07004751 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004752 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004753
David Ahernbeb1afac52017-02-02 12:37:09 -08004754 /* For multipath routes, walk the siblings list and add
4755 * each as a nexthop within RTA_MULTIPATH.
4756 */
Xin Long22d0bd82018-09-11 14:33:58 +08004757 if (rt6) {
4758 if (rt6_flags & RTF_GATEWAY &&
4759 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4760 goto nla_put_failure;
4761
4762 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4763 goto nla_put_failure;
4764 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004765 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004766 struct nlattr *mp;
4767
4768 mp = nla_nest_start(skb, RTA_MULTIPATH);
4769 if (!mp)
4770 goto nla_put_failure;
4771
David Ahernc0a72072019-04-02 14:11:58 -07004772 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4773 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004774 goto nla_put_failure;
4775
4776 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004777 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004778 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4779 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004780 goto nla_put_failure;
4781 }
4782
4783 nla_nest_end(skb, mp);
4784 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004785 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4786 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004787 goto nla_put_failure;
4788 }
4789
Xin Long22d0bd82018-09-11 14:33:58 +08004790 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004791 expires = dst ? dst->expires : rt->expires;
4792 expires -= jiffies;
4793 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004794
David Ahernd4ead6b2018-04-17 17:33:16 -07004795 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004796 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004797
Xin Long22d0bd82018-09-11 14:33:58 +08004798 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004799 goto nla_put_failure;
4800
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004801
Johannes Berg053c0952015-01-16 22:09:00 +01004802 nlmsg_end(skb, nlh);
4803 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004804
4805nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004806 nlmsg_cancel(skb, nlh);
4807 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004808}
4809
David Ahern13e38902018-10-15 18:56:44 -07004810static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4811 const struct net_device *dev)
4812{
David Ahernad1601a2019-03-27 20:53:56 -07004813 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004814 return true;
4815
4816 if (f6i->fib6_nsiblings) {
4817 struct fib6_info *sibling, *next_sibling;
4818
4819 list_for_each_entry_safe(sibling, next_sibling,
4820 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004821 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004822 return true;
4823 }
4824 }
4825
4826 return false;
4827}
4828
David Ahern8d1c8022018-04-17 17:33:26 -07004829int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004830{
4831 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004832 struct fib_dump_filter *filter = &arg->filter;
4833 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004834 struct net *net = arg->net;
4835
David Ahern421842e2018-04-17 17:33:18 -07004836 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004837 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004838
David Ahern13e38902018-10-15 18:56:44 -07004839 if ((filter->flags & RTM_F_PREFIX) &&
4840 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4841 /* success since this is not a prefix route */
4842 return 1;
4843 }
4844 if (filter->filter_set) {
4845 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4846 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4847 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004848 return 1;
4849 }
David Ahern13e38902018-10-15 18:56:44 -07004850 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004851 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852
David Ahernd4ead6b2018-04-17 17:33:16 -07004853 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4854 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004855 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004856}
4857
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004858static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4859 const struct nlmsghdr *nlh,
4860 struct nlattr **tb,
4861 struct netlink_ext_ack *extack)
4862{
4863 struct rtmsg *rtm;
4864 int i, err;
4865
4866 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4867 NL_SET_ERR_MSG_MOD(extack,
4868 "Invalid header for get route request");
4869 return -EINVAL;
4870 }
4871
4872 if (!netlink_strict_get_check(skb))
4873 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4874 rtm_ipv6_policy, extack);
4875
4876 rtm = nlmsg_data(nlh);
4877 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4878 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4879 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4880 rtm->rtm_type) {
4881 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4882 return -EINVAL;
4883 }
4884 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4885 NL_SET_ERR_MSG_MOD(extack,
4886 "Invalid flags for get route request");
4887 return -EINVAL;
4888 }
4889
4890 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4891 rtm_ipv6_policy, extack);
4892 if (err)
4893 return err;
4894
4895 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4896 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4897 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4898 return -EINVAL;
4899 }
4900
4901 for (i = 0; i <= RTA_MAX; i++) {
4902 if (!tb[i])
4903 continue;
4904
4905 switch (i) {
4906 case RTA_SRC:
4907 case RTA_DST:
4908 case RTA_IIF:
4909 case RTA_OIF:
4910 case RTA_MARK:
4911 case RTA_UID:
4912 case RTA_SPORT:
4913 case RTA_DPORT:
4914 case RTA_IP_PROTO:
4915 break;
4916 default:
4917 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4918 return -EINVAL;
4919 }
4920 }
4921
4922 return 0;
4923}
4924
David Ahernc21ef3e2017-04-16 09:48:24 -07004925static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4926 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004927{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004928 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004929 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004930 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004931 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004932 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004933 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004934 struct sk_buff *skb;
4935 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004936 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004937 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004938
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004939 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004940 if (err < 0)
4941 goto errout;
4942
4943 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004944 rtm = nlmsg_data(nlh);
4945 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004946 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004947
4948 if (tb[RTA_SRC]) {
4949 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4950 goto errout;
4951
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004952 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004953 }
4954
4955 if (tb[RTA_DST]) {
4956 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4957 goto errout;
4958
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004959 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004960 }
4961
4962 if (tb[RTA_IIF])
4963 iif = nla_get_u32(tb[RTA_IIF]);
4964
4965 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004966 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004967
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004968 if (tb[RTA_MARK])
4969 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4970
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004971 if (tb[RTA_UID])
4972 fl6.flowi6_uid = make_kuid(current_user_ns(),
4973 nla_get_u32(tb[RTA_UID]));
4974 else
4975 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4976
Roopa Prabhueacb9382018-05-22 14:03:28 -07004977 if (tb[RTA_SPORT])
4978 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4979
4980 if (tb[RTA_DPORT])
4981 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4982
4983 if (tb[RTA_IP_PROTO]) {
4984 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004985 &fl6.flowi6_proto, AF_INET6,
4986 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004987 if (err)
4988 goto errout;
4989 }
4990
Thomas Grafab364a62006-08-22 00:01:47 -07004991 if (iif) {
4992 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004993 int flags = 0;
4994
Florian Westphal121622d2017-08-15 16:34:42 +02004995 rcu_read_lock();
4996
4997 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07004998 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02004999 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07005000 err = -ENODEV;
5001 goto errout;
5002 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005003
5004 fl6.flowi6_iif = iif;
5005
5006 if (!ipv6_addr_any(&fl6.saddr))
5007 flags |= RT6_LOOKUP_F_HAS_SADDR;
5008
David Ahernb75cc8f2018-03-02 08:32:17 -08005009 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02005010
5011 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005012 } else {
5013 fl6.flowi6_oif = oif;
5014
Ido Schimmel58acfd72017-12-20 12:28:25 +02005015 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005016 }
5017
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005018
5019 rt = container_of(dst, struct rt6_info, dst);
5020 if (rt->dst.error) {
5021 err = rt->dst.error;
5022 ip6_rt_put(rt);
5023 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005024 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025
WANG Cong9d6acb32017-03-01 20:48:39 -08005026 if (rt == net->ipv6.ip6_null_entry) {
5027 err = rt->dst.error;
5028 ip6_rt_put(rt);
5029 goto errout;
5030 }
5031
Linus Torvalds1da177e2005-04-16 15:20:36 -07005032 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005033 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005034 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005035 err = -ENOBUFS;
5036 goto errout;
5037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005038
Changli Gaod8d1f302010-06-10 23:31:35 -07005039 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005040
5041 rcu_read_lock();
5042 from = rcu_dereference(rt->from);
5043
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005044 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005045 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005046 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5047 nlh->nlmsg_seq, 0);
5048 else
David Aherna68886a2018-04-20 15:38:02 -07005049 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5050 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005051 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5052 0);
David Aherna68886a2018-04-20 15:38:02 -07005053 rcu_read_unlock();
5054
Linus Torvalds1da177e2005-04-16 15:20:36 -07005055 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005056 kfree_skb(skb);
5057 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005058 }
5059
Eric W. Biederman15e47302012-09-07 20:12:54 +00005060 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005061errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005062 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005063}
5064
David Ahern8d1c8022018-04-17 17:33:26 -07005065void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005066 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005067{
5068 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005069 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005070 u32 seq;
5071 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005073 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005074 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005075
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005076 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005077 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005078 goto errout;
5079
David Ahernd4ead6b2018-04-17 17:33:16 -07005080 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5081 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005082 if (err < 0) {
5083 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5084 WARN_ON(err == -EMSGSIZE);
5085 kfree_skb(skb);
5086 goto errout;
5087 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005088 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005089 info->nlh, gfp_any());
5090 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005091errout:
5092 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005093 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005094}
5095
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005096static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005097 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005098{
Jiri Pirko351638e2013-05-28 01:30:21 +00005099 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005100 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005101
WANG Cong242d3a42017-05-08 10:12:13 -07005102 if (!(dev->flags & IFF_LOOPBACK))
5103 return NOTIFY_OK;
5104
5105 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005106 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005107 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005108 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5109#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005110 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005111 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005112 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005113 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5114#endif
WANG Cong76da0702017-06-20 11:42:27 -07005115 } else if (event == NETDEV_UNREGISTER &&
5116 dev->reg_state != NETREG_UNREGISTERED) {
5117 /* NETDEV_UNREGISTER could be fired for multiple times by
5118 * netdev_wait_allrefs(). Make sure we only call this once.
5119 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005120 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005121#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005122 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5123 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005124#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005125 }
5126
5127 return NOTIFY_OK;
5128}
5129
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130/*
5131 * /proc
5132 */
5133
5134#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005135static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5136{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005137 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005138 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005139 net->ipv6.rt6_stats->fib_nodes,
5140 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005141 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005142 net->ipv6.rt6_stats->fib_rt_entries,
5143 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005144 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005145 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005146
5147 return 0;
5148}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005149#endif /* CONFIG_PROC_FS */
5150
5151#ifdef CONFIG_SYSCTL
5152
Linus Torvalds1da177e2005-04-16 15:20:36 -07005153static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005154int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005155 void __user *buffer, size_t *lenp, loff_t *ppos)
5156{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005157 struct net *net;
5158 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005159 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005160 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005161 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005162
5163 net = (struct net *)ctl->extra1;
5164 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005165 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5166 if (ret)
5167 return ret;
5168
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005169 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005170 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005171}
5172
David Ahern7c6bb7d2018-10-11 20:17:21 -07005173static int zero;
5174static int one = 1;
5175
David Aherned792e22018-10-08 14:06:34 -07005176static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005177 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005178 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005179 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005181 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005182 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005183 },
5184 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005186 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005187 .maxlen = sizeof(int),
5188 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005189 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 },
5191 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005193 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 .maxlen = sizeof(int),
5195 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005196 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005197 },
5198 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005199 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005200 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005201 .maxlen = sizeof(int),
5202 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005203 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204 },
5205 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005206 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005207 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005208 .maxlen = sizeof(int),
5209 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005210 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005211 },
5212 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005213 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005214 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005215 .maxlen = sizeof(int),
5216 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005217 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005218 },
5219 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005220 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005221 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005222 .maxlen = sizeof(int),
5223 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005224 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005225 },
5226 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005227 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005228 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005229 .maxlen = sizeof(int),
5230 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005231 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005232 },
5233 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005234 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005235 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005236 .maxlen = sizeof(int),
5237 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005238 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005239 },
5240 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005241 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005242 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005243 .maxlen = sizeof(int),
5244 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005245 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005247 {
5248 .procname = "skip_notify_on_dev_down",
5249 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5250 .maxlen = sizeof(int),
5251 .mode = 0644,
5252 .proc_handler = proc_dointvec,
5253 .extra1 = &zero,
5254 .extra2 = &one,
5255 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005256 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005257};
5258
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005259struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005260{
5261 struct ctl_table *table;
5262
5263 table = kmemdup(ipv6_route_table_template,
5264 sizeof(ipv6_route_table_template),
5265 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005266
5267 if (table) {
5268 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005269 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005270 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005271 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5272 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5273 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5274 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5275 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5276 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5277 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005278 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005279 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005280
5281 /* Don't export sysctls to unprivileged users */
5282 if (net->user_ns != &init_user_ns)
5283 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005284 }
5285
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005286 return table;
5287}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288#endif
5289
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005290static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005291{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005292 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005293
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005294 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5295 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005296
Eric Dumazetfc66f952010-10-08 06:37:34 +00005297 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5298 goto out_ip6_dst_ops;
5299
David Ahern421842e2018-04-17 17:33:18 -07005300 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5301 sizeof(*net->ipv6.fib6_null_entry),
5302 GFP_KERNEL);
5303 if (!net->ipv6.fib6_null_entry)
5304 goto out_ip6_dst_entries;
5305
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005306 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5307 sizeof(*net->ipv6.ip6_null_entry),
5308 GFP_KERNEL);
5309 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005310 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005311 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005312 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5313 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005314
5315#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005316 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005317 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5318 sizeof(*net->ipv6.ip6_prohibit_entry),
5319 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005320 if (!net->ipv6.ip6_prohibit_entry)
5321 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005322 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005323 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5324 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005325
5326 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5327 sizeof(*net->ipv6.ip6_blk_hole_entry),
5328 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005329 if (!net->ipv6.ip6_blk_hole_entry)
5330 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005331 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005332 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5333 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005334#endif
5335
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005336 net->ipv6.sysctl.flush_delay = 0;
5337 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5338 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5339 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5340 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5341 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5342 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5343 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005344 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005345
Benjamin Thery6891a342008-03-04 13:49:47 -08005346 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5347
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005348 ret = 0;
5349out:
5350 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005351
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005352#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5353out_ip6_prohibit_entry:
5354 kfree(net->ipv6.ip6_prohibit_entry);
5355out_ip6_null_entry:
5356 kfree(net->ipv6.ip6_null_entry);
5357#endif
David Ahern421842e2018-04-17 17:33:18 -07005358out_fib6_null_entry:
5359 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005360out_ip6_dst_entries:
5361 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005362out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005363 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005364}
5365
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005366static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005367{
David Ahern421842e2018-04-17 17:33:18 -07005368 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005369 kfree(net->ipv6.ip6_null_entry);
5370#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5371 kfree(net->ipv6.ip6_prohibit_entry);
5372 kfree(net->ipv6.ip6_blk_hole_entry);
5373#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005374 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005375}
5376
Thomas Grafd1896342012-06-18 12:08:33 +00005377static int __net_init ip6_route_net_init_late(struct net *net)
5378{
5379#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005380 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5381 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005382 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5383 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005384#endif
5385 return 0;
5386}
5387
5388static void __net_exit ip6_route_net_exit_late(struct net *net)
5389{
5390#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005391 remove_proc_entry("ipv6_route", net->proc_net);
5392 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005393#endif
5394}
5395
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005396static struct pernet_operations ip6_route_net_ops = {
5397 .init = ip6_route_net_init,
5398 .exit = ip6_route_net_exit,
5399};
5400
David S. Millerc3426b42012-06-09 16:27:05 -07005401static int __net_init ipv6_inetpeer_init(struct net *net)
5402{
5403 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5404
5405 if (!bp)
5406 return -ENOMEM;
5407 inet_peer_base_init(bp);
5408 net->ipv6.peers = bp;
5409 return 0;
5410}
5411
5412static void __net_exit ipv6_inetpeer_exit(struct net *net)
5413{
5414 struct inet_peer_base *bp = net->ipv6.peers;
5415
5416 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005417 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005418 kfree(bp);
5419}
5420
David S. Miller2b823f72012-06-09 19:00:16 -07005421static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005422 .init = ipv6_inetpeer_init,
5423 .exit = ipv6_inetpeer_exit,
5424};
5425
Thomas Grafd1896342012-06-18 12:08:33 +00005426static struct pernet_operations ip6_route_net_late_ops = {
5427 .init = ip6_route_net_init_late,
5428 .exit = ip6_route_net_exit_late,
5429};
5430
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005431static struct notifier_block ip6_route_dev_notifier = {
5432 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005433 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005434};
5435
WANG Cong2f460932017-05-03 22:07:31 -07005436void __init ip6_route_init_special_entries(void)
5437{
5438 /* Registering of the loopback is done before this portion of code,
5439 * the loopback reference in rt6_info will not be taken, do it
5440 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005441 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005442 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5443 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5444 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5445 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5446 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5447 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5448 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5449 #endif
5450}
5451
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005452int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005453{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005454 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005455 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005456
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005457 ret = -ENOMEM;
5458 ip6_dst_ops_template.kmem_cachep =
5459 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5460 SLAB_HWCACHE_ALIGN, NULL);
5461 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005462 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005463
Eric Dumazetfc66f952010-10-08 06:37:34 +00005464 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005465 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005466 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005467
David S. Millerc3426b42012-06-09 16:27:05 -07005468 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5469 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005470 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005471
David S. Miller7e52b332012-06-15 15:51:55 -07005472 ret = register_pernet_subsys(&ip6_route_net_ops);
5473 if (ret)
5474 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005475
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005476 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5477
David S. Millere8803b62012-06-16 01:12:19 -07005478 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005479 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005480 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005481
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005482 ret = xfrm6_init();
5483 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005484 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005485
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005486 ret = fib6_rules_init();
5487 if (ret)
5488 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005489
Thomas Grafd1896342012-06-18 12:08:33 +00005490 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5491 if (ret)
5492 goto fib6_rules_init;
5493
Florian Westphal16feebc2017-12-02 21:44:08 +01005494 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5495 inet6_rtm_newroute, NULL, 0);
5496 if (ret < 0)
5497 goto out_register_late_subsys;
5498
5499 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5500 inet6_rtm_delroute, NULL, 0);
5501 if (ret < 0)
5502 goto out_register_late_subsys;
5503
5504 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5505 inet6_rtm_getroute, NULL,
5506 RTNL_FLAG_DOIT_UNLOCKED);
5507 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005508 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005509
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005510 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005511 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005512 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005513
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005514 for_each_possible_cpu(cpu) {
5515 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5516
5517 INIT_LIST_HEAD(&ul->head);
5518 spin_lock_init(&ul->lock);
5519 }
5520
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005521out:
5522 return ret;
5523
Thomas Grafd1896342012-06-18 12:08:33 +00005524out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005525 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005526 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005527fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005528 fib6_rules_cleanup();
5529xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005530 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005531out_fib6_init:
5532 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005533out_register_subsys:
5534 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005535out_register_inetpeer:
5536 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005537out_dst_entries:
5538 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005539out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005540 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005541 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005542}
5543
5544void ip6_route_cleanup(void)
5545{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005546 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005547 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005548 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005549 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005550 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005551 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005552 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005553 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005554 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005555}