blob: 0ad77b62da7c68ba0caa0a15ab07cedcaef8e77c [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 Ahern8d1c8022018-04-17 17:33:26 -0700113static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt,
Wei Wang35732d02017-10-06 12:05:57 -0700114 struct in6_addr *daddr,
115 struct in6_addr *saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800117#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -0700118static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000119 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700120 const struct in6_addr *gwaddr,
121 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +0000122 unsigned int pref);
David Ahern8d1c8022018-04-17 17:33:26 -0700123static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000124 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700125 const struct in6_addr *gwaddr,
126 struct net_device *dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800127#endif
128
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700129struct uncached_list {
130 spinlock_t lock;
131 struct list_head head;
132};
133
134static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
135
Xin Long510c3212018-02-14 19:06:02 +0800136void rt6_uncached_list_add(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700137{
138 struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
139
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700140 rt->rt6i_uncached_list = ul;
141
142 spin_lock_bh(&ul->lock);
143 list_add_tail(&rt->rt6i_uncached, &ul->head);
144 spin_unlock_bh(&ul->lock);
145}
146
Xin Long510c3212018-02-14 19:06:02 +0800147void rt6_uncached_list_del(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700148{
149 if (!list_empty(&rt->rt6i_uncached)) {
150 struct uncached_list *ul = rt->rt6i_uncached_list;
Wei Wang81eb8442017-10-06 12:06:11 -0700151 struct net *net = dev_net(rt->dst.dev);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700152
153 spin_lock_bh(&ul->lock);
154 list_del(&rt->rt6i_uncached);
Wei Wang81eb8442017-10-06 12:06:11 -0700155 atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700156 spin_unlock_bh(&ul->lock);
157 }
158}
159
160static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
161{
162 struct net_device *loopback_dev = net->loopback_dev;
163 int cpu;
164
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500165 if (dev == loopback_dev)
166 return;
167
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700168 for_each_possible_cpu(cpu) {
169 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
170 struct rt6_info *rt;
171
172 spin_lock_bh(&ul->lock);
173 list_for_each_entry(rt, &ul->head, rt6i_uncached) {
174 struct inet6_dev *rt_idev = rt->rt6i_idev;
175 struct net_device *rt_dev = rt->dst.dev;
176
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500177 if (rt_idev->dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700178 rt->rt6i_idev = in6_dev_get(loopback_dev);
179 in6_dev_put(rt_idev);
180 }
181
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500182 if (rt_dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700183 rt->dst.dev = loopback_dev;
184 dev_hold(rt->dst.dev);
185 dev_put(rt_dev);
186 }
187 }
188 spin_unlock_bh(&ul->lock);
189 }
190}
191
David Ahernf8a1b432018-04-17 17:33:21 -0700192static inline const void *choose_neigh_daddr(const struct in6_addr *p,
David S. Millerf894cbf2012-07-02 21:52:24 -0700193 struct sk_buff *skb,
194 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500195{
David S. Millera7563f32012-01-26 16:29:16 -0500196 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500197 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700198 else if (skb)
199 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500200 return daddr;
201}
202
David Ahernf8a1b432018-04-17 17:33:21 -0700203struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
204 struct net_device *dev,
205 struct sk_buff *skb,
206 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207{
David S. Miller39232972012-01-26 15:22:32 -0500208 struct neighbour *n;
209
David Ahernf8a1b432018-04-17 17:33:21 -0700210 daddr = choose_neigh_daddr(gw, skb, daddr);
211 n = __ipv6_neigh_lookup(dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500212 if (n)
213 return n;
Stefano Brivio7adf3242019-01-02 13:29:27 +0100214
215 n = neigh_create(&nd_tbl, daddr, dev);
216 return IS_ERR(n) ? NULL : n;
David Ahernf8a1b432018-04-17 17:33:21 -0700217}
218
219static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
220 struct sk_buff *skb,
221 const void *daddr)
222{
223 const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
224
225 return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500226}
227
Julian Anastasov63fca652017-02-06 23:14:15 +0200228static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
229{
230 struct net_device *dev = dst->dev;
231 struct rt6_info *rt = (struct rt6_info *)dst;
232
David Ahernf8a1b432018-04-17 17:33:21 -0700233 daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200234 if (!daddr)
235 return;
236 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
237 return;
238 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
239 return;
240 __ipv6_confirm_neigh(dev, daddr);
241}
242
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800243static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .gc = ip6_dst_gc,
246 .gc_thresh = 1024,
247 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800248 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000249 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700250 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .destroy = ip6_dst_destroy,
252 .ifdown = ip6_dst_ifdown,
253 .negative_advice = ip6_negative_advice,
254 .link_failure = ip6_link_failure,
255 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700256 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500257 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700258 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200259 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Steffen Klassertebb762f2011-11-23 02:12:51 +0000262static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800263{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000264 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
265
266 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800267}
268
David S. Miller6700c272012-07-17 03:29:28 -0700269static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
270 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700271{
272}
273
David S. Miller6700c272012-07-17 03:29:28 -0700274static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
275 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700276{
277}
278
David S. Miller14e50e52007-05-24 18:17:54 -0700279static struct dst_ops ip6_dst_blackhole_ops = {
280 .family = AF_INET6,
David S. Miller14e50e52007-05-24 18:17:54 -0700281 .destroy = ip6_dst_destroy,
282 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000283 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800284 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700285 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700286 .redirect = ip6_rt_blackhole_redirect,
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -0700287 .cow_metrics = dst_cow_metrics_generic,
David Ahernf8a1b432018-04-17 17:33:21 -0700288 .neigh_lookup = ip6_dst_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700289};
290
David S. Miller62fa8a82011-01-26 20:51:05 -0800291static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800292 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800293};
294
David Ahern8d1c8022018-04-17 17:33:26 -0700295static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700296 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
297 .fib6_protocol = RTPROT_KERNEL,
298 .fib6_metric = ~(u32)0,
299 .fib6_ref = ATOMIC_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700300 .fib6_type = RTN_UNREACHABLE,
301 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
302};
303
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000304static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700305 .dst = {
306 .__refcnt = ATOMIC_INIT(1),
307 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000308 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700309 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700310 .input = ip6_pkt_discard,
311 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 },
313 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314};
315
Thomas Graf101367c2006-08-04 03:39:02 -0700316#ifdef CONFIG_IPV6_MULTIPLE_TABLES
317
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000318static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .dst = {
320 .__refcnt = ATOMIC_INIT(1),
321 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000322 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700323 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700324 .input = ip6_pkt_prohibit,
325 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700326 },
327 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700328};
329
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000330static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700331 .dst = {
332 .__refcnt = ATOMIC_INIT(1),
333 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000334 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700335 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700336 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500337 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700338 },
339 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700340};
341
342#endif
343
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344static void rt6_info_init(struct rt6_info *rt)
345{
346 struct dst_entry *dst = &rt->dst;
347
348 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700349 INIT_LIST_HEAD(&rt->rt6i_uncached);
350}
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700353struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
354 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David S. Miller97bab732012-06-09 22:36:36 -0700356 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700357 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700358
Wei Wang81eb8442017-10-06 12:06:11 -0700359 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700360 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700361 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
362 }
Steffen Klassert81048912012-07-05 23:37:09 +0000363
David S. Millercf911662011-04-28 14:31:47 -0700364 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
David Ahern9ab179d2016-04-07 11:10:06 -0700366EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368static void ip6_dst_destroy(struct dst_entry *dst)
369{
370 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700371 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700372 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
David Ahern1620a332018-10-04 20:07:54 -0700374 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700375 rt6_uncached_list_del(rt);
376
377 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500378 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rt->rt6i_idev = NULL;
380 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900381 }
Gao feng1716a962012-04-06 00:13:10 +0000382
David Aherna68886a2018-04-20 15:38:02 -0700383 rcu_read_lock();
384 from = rcu_dereference(rt->from);
385 rcu_assign_pointer(rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700386 fib6_info_release(from);
David Aherna68886a2018-04-20 15:38:02 -0700387 rcu_read_unlock();
David S. Millerb3419362010-11-30 12:27:11 -0800388}
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
391 int how)
392{
393 struct rt6_info *rt = (struct rt6_info *)dst;
394 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800395 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900396 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Wei Wange5645f52017-08-14 10:44:59 -0700398 if (idev && idev->dev != loopback_dev) {
399 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
400 if (loopback_idev) {
401 rt->rt6i_idev = loopback_idev;
402 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405}
406
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800407static bool __rt6_check_expired(const struct rt6_info *rt)
408{
409 if (rt->rt6i_flags & RTF_EXPIRES)
410 return time_after(jiffies, rt->dst.expires);
411 else
412 return false;
413}
414
Eric Dumazeta50feda2012-05-18 18:57:34 +0000415static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
David Aherna68886a2018-04-20 15:38:02 -0700417 struct fib6_info *from;
418
419 from = rcu_dereference(rt->from);
420
Gao feng1716a962012-04-06 00:13:10 +0000421 if (rt->rt6i_flags & RTF_EXPIRES) {
422 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000423 return true;
David Aherna68886a2018-04-20 15:38:02 -0700424 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800425 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700426 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000427 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000428 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
David Ahernb1d40992019-04-16 14:35:59 -0700431void fib6_select_path(const struct net *net, struct fib6_result *res,
432 struct flowi6 *fl6, int oif, bool have_oif_match,
433 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000434{
David Ahern8d1c8022018-04-17 17:33:26 -0700435 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700436 struct fib6_info *match = res->f6i;
437
438 if (!match->fib6_nsiblings || have_oif_match)
439 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000440
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200441 /* We might have already computed the hash for ICMPv6 errors. In such
442 * case it will always be non-zero. Otherwise now is the time to do it.
443 */
444 if (!fl6->mp_hash)
David Ahernb4bac172018-03-02 08:32:18 -0800445 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200446
David Ahernad1601a2019-03-27 20:53:56 -0700447 if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700448 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200449
David Ahern93c2fb22018-04-18 15:38:59 -0700450 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
451 fib6_siblings) {
David Ahern702cea52019-04-09 14:41:13 -0700452 const struct fib6_nh *nh = &sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700453 int nh_upper_bound;
454
David Ahern702cea52019-04-09 14:41:13 -0700455 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700456 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200457 continue;
David Ahern702cea52019-04-09 14:41:13 -0700458 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200459 break;
460 match = sibling;
461 break;
462 }
463
David Ahernb1d40992019-04-16 14:35:59 -0700464out:
465 res->f6i = match;
466 res->nh = &match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000467}
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700470 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 */
472
David Ahern0c59d002019-04-09 14:41:18 -0700473static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
474 const struct in6_addr *saddr, int oif, int flags)
475{
476 const struct net_device *dev;
477
478 if (nh->fib_nh_flags & RTNH_F_DEAD)
479 return false;
480
481 dev = nh->fib_nh_dev;
482 if (oif) {
483 if (dev->ifindex == oif)
484 return true;
485 } else {
486 if (ipv6_chk_addr(net, saddr, dev,
487 flags & RT6_LOOKUP_F_IFACE))
488 return true;
489 }
490
491 return false;
492}
493
David Ahern8d1c8022018-04-17 17:33:26 -0700494static inline struct fib6_info *rt6_device_match(struct net *net,
495 struct fib6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000496 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700498 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
David Ahern0c59d002019-04-09 14:41:18 -0700500 const struct fib6_nh *nh;
David Ahern8d1c8022018-04-17 17:33:26 -0700501 struct fib6_info *sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
David Ahern5e670d82018-04-17 17:33:14 -0700503 if (!oif && ipv6_addr_any(saddr) &&
David Ahernad1601a2019-03-27 20:53:56 -0700504 !(rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD))
Ido Schimmel8067bb82018-01-07 12:45:09 +0200505 return rt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900506
David Ahern8fb11a92018-05-04 13:54:24 -0700507 for (sprt = rt; sprt; sprt = rcu_dereference(sprt->fib6_next)) {
David Ahern0c59d002019-04-09 14:41:18 -0700508 nh = &sprt->fib6_nh;
509 if (__rt6_device_match(net, nh, saddr, oif, flags))
510 return sprt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
David Aherneea68cd2018-04-18 15:39:02 -0700513 if (oif && flags & RT6_LOOKUP_F_IFACE)
514 return net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
David Ahernad1601a2019-03-27 20:53:56 -0700516 return rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800519#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200520struct __rt6_probe_work {
521 struct work_struct work;
522 struct in6_addr target;
523 struct net_device *dev;
524};
525
526static void rt6_probe_deferred(struct work_struct *w)
527{
528 struct in6_addr mcaddr;
529 struct __rt6_probe_work *work =
530 container_of(w, struct __rt6_probe_work, work);
531
532 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800533 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200534 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100535 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200536}
537
David Aherncc3a86c2019-04-09 14:41:12 -0700538static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800539{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200540 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700541 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000542 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700543 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200544 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700545
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800546 /*
547 * Okay, this does not seem to be appropriate
548 * for now, however, we need to check if it
549 * is really so; aka Router Reachability Probing.
550 *
551 * Router Reachability Probe MUST be rate-limited
552 * to no more than one per minute.
553 */
David Aherncc3a86c2019-04-09 14:41:12 -0700554 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000555 return;
David Ahern5e670d82018-04-17 17:33:14 -0700556
David Aherncc3a86c2019-04-09 14:41:12 -0700557 nh_gw = &fib6_nh->fib_nh_gw6;
558 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000559 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200560 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700561 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000562 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700563 if (neigh->nud_state & NUD_VALID)
564 goto out;
565
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000566 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700567 if (!(neigh->nud_state & NUD_VALID) &&
568 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700569 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700570 work = kmalloc(sizeof(*work), GFP_ATOMIC);
571 if (work)
572 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200573 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000574 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700575 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200576 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700577 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000578 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700579
580 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700581 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700582 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700583 work->target = *nh_gw;
584 dev_hold(dev);
585 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700586 schedule_work(&work->work);
587 }
588
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700589out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000590 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800591}
592#else
David Aherncc3a86c2019-04-09 14:41:12 -0700593static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800594{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800595}
596#endif
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800599 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 */
David Ahern1ba9a892019-04-09 14:41:10 -0700601static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200603 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700604 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000605
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000606 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700607 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
608 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000609 if (neigh) {
610 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800611 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200612 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800613#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000614 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200615 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100616 else
617 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800618#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000619 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200620 } else {
621 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100622 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000623 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000624 rcu_read_unlock_bh();
625
Paul Marksa5a81f02012-12-03 10:26:54 +0000626 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800627}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
David Ahern702cea52019-04-09 14:41:13 -0700629static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
630 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800631{
David Ahern6e1809a2019-04-09 14:41:11 -0700632 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900633
David Ahern6e1809a2019-04-09 14:41:11 -0700634 if (!oif || nh->fib_nh_dev->ifindex == oif)
635 m = 2;
636
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700637 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200638 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800639#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700640 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800641#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700642 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700643 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700644 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200645 if (n < 0)
646 return n;
647 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800648 return m;
649}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
David Ahern28679ed2019-04-09 14:41:14 -0700651static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
652 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800653{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200654 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700655 bool rc = false;
656 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400657
David Ahern28679ed2019-04-09 14:41:14 -0700658 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200659 goto out;
660
David Ahern28679ed2019-04-09 14:41:14 -0700661 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
662 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700663 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400664 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700665
David Ahern28679ed2019-04-09 14:41:14 -0700666 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100667 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200668 match_do_rr = true;
669 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100670 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700671 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700672 }
673
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200674 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700675 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200676
Jiri Benc7e980562013-12-11 13:48:20 +0100677 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200678 if (m > *mpri) {
679 *do_rr = match_do_rr;
680 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700681 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200682 }
David S. Millerf11e6652007-03-24 20:36:25 -0700683out:
David Ahern28679ed2019-04-09 14:41:14 -0700684 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700685}
686
David Ahern30c15f02019-04-09 14:41:15 -0700687static void __find_rr_leaf(struct fib6_info *rt_start,
688 struct fib6_info *nomatch, u32 metric,
689 struct fib6_info **match, struct fib6_info **cont,
690 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700691{
David Ahern30c15f02019-04-09 14:41:15 -0700692 struct fib6_info *rt;
693
694 for (rt = rt_start;
695 rt && rt != nomatch;
696 rt = rcu_dereference(rt->fib6_next)) {
697 struct fib6_nh *nh;
698
699 if (cont && rt->fib6_metric != metric) {
700 *cont = rt;
701 return;
702 }
703
704 if (fib6_check_expired(rt))
705 continue;
706
707 nh = &rt->fib6_nh;
708 if (find_match(nh, rt->fib6_flags, oif, strict, mpri, do_rr))
709 *match = rt;
710 }
711}
712
713static struct fib6_info *find_rr_leaf(struct fib6_node *fn,
714 struct fib6_info *leaf,
715 struct fib6_info *rr_head,
716 u32 metric, int oif, int strict,
717 bool *do_rr)
718{
719 struct fib6_info *match = NULL, *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800720 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
David Ahern30c15f02019-04-09 14:41:15 -0700722 __find_rr_leaf(rr_head, NULL, metric, &match, &cont,
723 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700724
David Ahern30c15f02019-04-09 14:41:15 -0700725 __find_rr_leaf(leaf, rr_head, metric, &match, &cont,
726 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700727
728 if (match || !cont)
729 return match;
730
David Ahern30c15f02019-04-09 14:41:15 -0700731 __find_rr_leaf(cont, NULL, metric, &match, NULL,
732 oif, strict, do_rr, &mpri);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800733
David S. Millerf11e6652007-03-24 20:36:25 -0700734 return match;
735}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800736
David Ahern8d1c8022018-04-17 17:33:26 -0700737static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
Wei Wang8d1040e2017-10-06 12:06:08 -0700738 int oif, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700739{
David Ahern8d1c8022018-04-17 17:33:26 -0700740 struct fib6_info *leaf = rcu_dereference(fn->leaf);
741 struct fib6_info *match, *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200742 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700743 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
David Ahern421842e2018-04-17 17:33:18 -0700745 if (!leaf || leaf == net->ipv6.fib6_null_entry)
746 return net->ipv6.fib6_null_entry;
Wei Wang8d1040e2017-10-06 12:06:08 -0700747
Wei Wang66f5d6c2017-10-06 12:06:10 -0700748 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700749 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700750 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Wei Wang17ecf592017-10-06 12:06:09 -0700752 /* Double check to make sure fn is not an intermediate node
753 * and fn->leaf does not points to its child's leaf
754 * (This might happen if all routes under fn are deleted from
755 * the tree and fib6_repair_tree() is called on the node.)
756 */
David Ahern93c2fb22018-04-18 15:38:59 -0700757 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700758#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700759 if (rt0->fib6_src.plen)
760 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700761#endif
762 if (fn->fn_bit != key_plen)
David Ahern421842e2018-04-17 17:33:18 -0700763 return net->ipv6.fib6_null_entry;
Wei Wang17ecf592017-10-06 12:06:09 -0700764
David Ahern93c2fb22018-04-18 15:38:59 -0700765 match = find_rr_leaf(fn, leaf, rt0, rt0->fib6_metric, oif, strict,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200766 &do_rr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200768 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700769 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700770
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800771 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700772 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700773 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700774
Wei Wang66f5d6c2017-10-06 12:06:10 -0700775 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700776 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700777 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700778 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700779 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700780 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700781 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 }
783
David Ahern421842e2018-04-17 17:33:18 -0700784 return match ? match : net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785}
786
David Ahern8d1c8022018-04-17 17:33:26 -0700787static bool rt6_is_gw_or_nonexthop(const struct fib6_info *rt)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700788{
David Ahernbdf00462019-04-05 16:30:26 -0700789 return (rt->fib6_flags & RTF_NONEXTHOP) || rt->fib6_nh.fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700790}
791
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800792#ifdef CONFIG_IPV6_ROUTE_INFO
793int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000794 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800795{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900796 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800797 struct route_info *rinfo = (struct route_info *) opt;
798 struct in6_addr prefix_buf, *prefix;
799 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900800 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700801 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800802
803 if (len < sizeof(struct route_info)) {
804 return -EINVAL;
805 }
806
807 /* Sanity check for prefix_len and length */
808 if (rinfo->length > 3) {
809 return -EINVAL;
810 } else if (rinfo->prefix_len > 128) {
811 return -EINVAL;
812 } else if (rinfo->prefix_len > 64) {
813 if (rinfo->length < 2) {
814 return -EINVAL;
815 }
816 } else if (rinfo->prefix_len > 0) {
817 if (rinfo->length < 1) {
818 return -EINVAL;
819 }
820 }
821
822 pref = rinfo->route_pref;
823 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000824 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800825
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900826 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800827
828 if (rinfo->length == 3)
829 prefix = (struct in6_addr *)rinfo->prefix;
830 else {
831 /* this function is safe */
832 ipv6_addr_prefix(&prefix_buf,
833 (struct in6_addr *)rinfo->prefix,
834 rinfo->prefix_len);
835 prefix = &prefix_buf;
836 }
837
Duan Jiongf104a562013-11-08 09:56:53 +0800838 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700839 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800840 else
841 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700842 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800843
844 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700845 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800846 rt = NULL;
847 }
848
849 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700850 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
851 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800852 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700853 rt->fib6_flags = RTF_ROUTEINFO |
854 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800855
856 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000857 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700858 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000859 else
David Ahern14895682018-04-17 17:33:17 -0700860 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000861
David Ahern93531c62018-04-17 17:33:25 -0700862 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800863 }
864 return 0;
865}
866#endif
867
David Ahernae90d862018-04-17 17:33:12 -0700868/*
869 * Misc support functions
870 */
871
872/* called with rcu_lock held */
David Ahern8d1c8022018-04-17 17:33:26 -0700873static struct net_device *ip6_rt_get_dev_rcu(struct fib6_info *rt)
David Ahernae90d862018-04-17 17:33:12 -0700874{
David Ahernad1601a2019-03-27 20:53:56 -0700875 struct net_device *dev = rt->fib6_nh.fib_nh_dev;
David Ahernae90d862018-04-17 17:33:12 -0700876
David Ahern93c2fb22018-04-18 15:38:59 -0700877 if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700878 /* for copies of local routes, dst->dev needs to be the
879 * device if it is a master device, the master device if
880 * device is enslaved, and the loopback as the default
881 */
882 if (netif_is_l3_slave(dev) &&
David Ahern93c2fb22018-04-18 15:38:59 -0700883 !rt6_need_strict(&rt->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700884 dev = l3mdev_master_dev_rcu(dev);
885 else if (!netif_is_l3_master(dev))
886 dev = dev_net(dev)->loopback_dev;
887 /* last case is netif_is_l3_master(dev) is true in which
888 * case we want dev returned to be dev
889 */
890 }
891
892 return dev;
893}
894
David Ahern6edb3c92018-04-17 17:33:15 -0700895static const int fib6_prop[RTN_MAX + 1] = {
896 [RTN_UNSPEC] = 0,
897 [RTN_UNICAST] = 0,
898 [RTN_LOCAL] = 0,
899 [RTN_BROADCAST] = 0,
900 [RTN_ANYCAST] = 0,
901 [RTN_MULTICAST] = 0,
902 [RTN_BLACKHOLE] = -EINVAL,
903 [RTN_UNREACHABLE] = -EHOSTUNREACH,
904 [RTN_PROHIBIT] = -EACCES,
905 [RTN_THROW] = -EAGAIN,
906 [RTN_NAT] = -EINVAL,
907 [RTN_XRESOLVE] = -EINVAL,
908};
909
910static int ip6_rt_type_to_error(u8 fib6_type)
911{
912 return fib6_prop[fib6_type];
913}
914
David Ahern8d1c8022018-04-17 17:33:26 -0700915static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700916{
917 unsigned short flags = 0;
918
919 if (rt->dst_nocount)
920 flags |= DST_NOCOUNT;
921 if (rt->dst_nopolicy)
922 flags |= DST_NOPOLICY;
923 if (rt->dst_host)
924 flags |= DST_HOST;
925
926 return flags;
927}
928
David Ahern8d1c8022018-04-17 17:33:26 -0700929static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700930{
931 rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
932
933 switch (ort->fib6_type) {
934 case RTN_BLACKHOLE:
935 rt->dst.output = dst_discard_out;
936 rt->dst.input = dst_discard;
937 break;
938 case RTN_PROHIBIT:
939 rt->dst.output = ip6_pkt_prohibit_out;
940 rt->dst.input = ip6_pkt_prohibit;
941 break;
942 case RTN_THROW:
943 case RTN_UNREACHABLE:
944 default:
945 rt->dst.output = ip6_pkt_discard_out;
946 rt->dst.input = ip6_pkt_discard;
947 break;
948 }
949}
950
David Ahern8d1c8022018-04-17 17:33:26 -0700951static void ip6_rt_init_dst(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700952{
David Ahern93c2fb22018-04-18 15:38:59 -0700953 if (ort->fib6_flags & RTF_REJECT) {
David Ahern6edb3c92018-04-17 17:33:15 -0700954 ip6_rt_init_dst_reject(rt, ort);
955 return;
956 }
957
958 rt->dst.error = 0;
959 rt->dst.output = ip6_output;
960
Hangbin Liud23c4b62018-08-23 11:31:37 +0800961 if (ort->fib6_type == RTN_LOCAL || ort->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700962 rt->dst.input = ip6_input;
David Ahern93c2fb22018-04-18 15:38:59 -0700963 } else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700964 rt->dst.input = ip6_mc_input;
965 } else {
966 rt->dst.input = ip6_forward;
967 }
968
David Ahernad1601a2019-03-27 20:53:56 -0700969 if (ort->fib6_nh.fib_nh_lws) {
970 rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700971 lwtunnel_set_redirect(&rt->dst);
972 }
973
974 rt->dst.lastuse = jiffies;
975}
976
Wei Wange873e4b2018-07-21 20:56:32 -0700977/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -0700978static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -0700979{
David Ahernae90d862018-04-17 17:33:12 -0700980 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -0700981 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -0700982 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -0700983}
984
Wei Wange873e4b2018-07-21 20:56:32 -0700985/* Caller must already hold reference to @ort */
David Ahern8d1c8022018-04-17 17:33:26 -0700986static void ip6_rt_copy_init(struct rt6_info *rt, struct fib6_info *ort)
David Ahernae90d862018-04-17 17:33:12 -0700987{
David Aherndcd1f572018-04-18 15:39:05 -0700988 struct net_device *dev = fib6_info_nh_dev(ort);
989
David Ahern6edb3c92018-04-17 17:33:15 -0700990 ip6_rt_init_dst(rt, ort);
991
David Ahern93c2fb22018-04-18 15:38:59 -0700992 rt->rt6i_dst = ort->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -0700993 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern93c2fb22018-04-18 15:38:59 -0700994 rt->rt6i_flags = ort->fib6_flags;
David Ahernbdf00462019-04-05 16:30:26 -0700995 if (ort->fib6_nh.fib_nh_gw_family) {
David Ahernad1601a2019-03-27 20:53:56 -0700996 rt->rt6i_gateway = ort->fib6_nh.fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -0700997 rt->rt6i_flags |= RTF_GATEWAY;
998 }
David Ahernae90d862018-04-17 17:33:12 -0700999 rt6_set_from(rt, ort);
David Ahernae90d862018-04-17 17:33:12 -07001000#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07001001 rt->rt6i_src = ort->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001002#endif
David Ahernae90d862018-04-17 17:33:12 -07001003}
1004
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001005static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1006 struct in6_addr *saddr)
1007{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001008 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001009 while (1) {
1010 if (fn->fn_flags & RTN_TL_ROOT)
1011 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001012 pn = rcu_dereference(fn->parent);
1013 sn = FIB6_SUBTREE(pn);
1014 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001015 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001016 else
1017 fn = pn;
1018 if (fn->fn_flags & RTN_RTINFO)
1019 return fn;
1020 }
1021}
Thomas Grafc71099a2006-08-04 23:20:06 -07001022
David Ahern10585b42019-03-20 09:24:50 -07001023static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001024{
1025 struct rt6_info *rt = *prt;
1026
1027 if (dst_hold_safe(&rt->dst))
1028 return true;
David Ahern10585b42019-03-20 09:24:50 -07001029 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001030 rt = net->ipv6.ip6_null_entry;
1031 dst_hold(&rt->dst);
1032 } else {
1033 rt = NULL;
1034 }
1035 *prt = rt;
1036 return false;
1037}
1038
David Aherndec9b0e2018-04-17 17:33:19 -07001039/* called with rcu_lock held */
David Ahern8d1c8022018-04-17 17:33:26 -07001040static struct rt6_info *ip6_create_rt_rcu(struct fib6_info *rt)
David Aherndec9b0e2018-04-17 17:33:19 -07001041{
David Ahern3b6761d2018-04-17 17:33:20 -07001042 unsigned short flags = fib6_info_dst_flags(rt);
David Ahernad1601a2019-03-27 20:53:56 -07001043 struct net_device *dev = rt->fib6_nh.fib_nh_dev;
David Aherndec9b0e2018-04-17 17:33:19 -07001044 struct rt6_info *nrt;
1045
Wei Wange873e4b2018-07-21 20:56:32 -07001046 if (!fib6_info_hold_safe(rt))
Xin Long1c87e792019-03-20 14:45:48 +08001047 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001048
David Ahern93531c62018-04-17 17:33:25 -07001049 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001050 if (!nrt) {
Wei Wange873e4b2018-07-21 20:56:32 -07001051 fib6_info_release(rt);
Xin Long1c87e792019-03-20 14:45:48 +08001052 goto fallback;
1053 }
David Aherndec9b0e2018-04-17 17:33:19 -07001054
Xin Long1c87e792019-03-20 14:45:48 +08001055 ip6_rt_copy_init(nrt, rt);
1056 return nrt;
1057
1058fallback:
1059 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1060 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001061 return nrt;
1062}
1063
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001064static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1065 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001066 struct flowi6 *fl6,
1067 const struct sk_buff *skb,
1068 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069{
David Ahernb1d40992019-04-16 14:35:59 -07001070 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001072 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
David Ahernb6cdbc82018-03-29 17:44:57 -07001074 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1075 flags &= ~RT6_LOOKUP_F_IFACE;
1076
Wei Wang66f5d6c2017-10-06 12:06:10 -07001077 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001078 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001079restart:
David Ahernb1d40992019-04-16 14:35:59 -07001080 res.f6i = rcu_dereference(fn->leaf);
1081 if (!res.f6i)
1082 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001083 else
David Ahernb1d40992019-04-16 14:35:59 -07001084 res.f6i = rt6_device_match(net, res.f6i, &fl6->saddr,
1085 fl6->flowi6_oif, flags);
David Ahernaf52a522019-04-09 14:41:16 -07001086
David Ahernb1d40992019-04-16 14:35:59 -07001087 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001088 fn = fib6_backtrack(fn, &fl6->saddr);
1089 if (fn)
1090 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001091
1092 rt = net->ipv6.ip6_null_entry;
1093 dst_hold(&rt->dst);
1094 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001095 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001096
David Ahernb1d40992019-04-16 14:35:59 -07001097 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1098 fl6->flowi6_oif != 0, skb, flags);
1099
David S. Miller4c9483b2011-03-12 16:22:43 -05001100 /* Search through exception table */
David Ahernb1d40992019-04-16 14:35:59 -07001101 rt = rt6_find_cached_rt(res.f6i, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001102 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001103 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001104 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001105 } else {
David Ahernb1d40992019-04-16 14:35:59 -07001106 rt = ip6_create_rt_rcu(res.f6i);
David Aherndec9b0e2018-04-17 17:33:19 -07001107 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001108
David Ahernaf52a522019-04-09 14:41:16 -07001109out:
David Ahernb1d40992019-04-16 14:35:59 -07001110 trace_fib6_table_lookup(net, res.f6i, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001111
Wei Wang66f5d6c2017-10-06 12:06:10 -07001112 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001113
Thomas Grafc71099a2006-08-04 23:20:06 -07001114 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001115}
1116
Ian Morris67ba4152014-08-24 21:53:10 +01001117struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001118 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001119{
David Ahernb75cc8f2018-03-02 08:32:17 -08001120 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001121}
1122EXPORT_SYMBOL_GPL(ip6_route_lookup);
1123
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001124struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001125 const struct in6_addr *saddr, int oif,
1126 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001127{
David S. Miller4c9483b2011-03-12 16:22:43 -05001128 struct flowi6 fl6 = {
1129 .flowi6_oif = oif,
1130 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001131 };
1132 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001133 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001134
Thomas Grafadaa70b2006-10-13 15:01:03 -07001135 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001136 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001137 flags |= RT6_LOOKUP_F_HAS_SADDR;
1138 }
1139
David Ahernb75cc8f2018-03-02 08:32:17 -08001140 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001141 if (dst->error == 0)
1142 return (struct rt6_info *) dst;
1143
1144 dst_release(dst);
1145
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 return NULL;
1147}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001148EXPORT_SYMBOL(rt6_lookup);
1149
Thomas Grafc71099a2006-08-04 23:20:06 -07001150/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001151 * It takes new route entry, the addition fails by any reason the
1152 * route is released.
1153 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 */
1155
David Ahern8d1c8022018-04-17 17:33:26 -07001156static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001157 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158{
1159 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001160 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
David Ahern93c2fb22018-04-18 15:38:59 -07001162 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001163 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001164 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001165 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166
1167 return err;
1168}
1169
David Ahern8d1c8022018-04-17 17:33:26 -07001170int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001171{
David Ahernafb1d4b52018-04-17 17:33:11 -07001172 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001173
David Ahernd4ead6b2018-04-17 17:33:16 -07001174 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001175}
1176
David Ahern8d1c8022018-04-17 17:33:26 -07001177static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001178 const struct in6_addr *daddr,
1179 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180{
David Ahern4832c302017-08-17 12:17:20 -07001181 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 struct rt6_info *rt;
1183
1184 /*
1185 * Clone the route.
1186 */
1187
Wei Wange873e4b2018-07-21 20:56:32 -07001188 if (!fib6_info_hold_safe(ort))
1189 return NULL;
1190
David Ahern4832c302017-08-17 12:17:20 -07001191 dev = ip6_rt_get_dev_rcu(ort);
David Ahern93531c62018-04-17 17:33:25 -07001192 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001193 if (!rt) {
1194 fib6_info_release(ort);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001195 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001196 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001197
1198 ip6_rt_copy_init(rt, ort);
1199 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001200 rt->dst.flags |= DST_HOST;
1201 rt->rt6i_dst.addr = *daddr;
1202 rt->rt6i_dst.plen = 128;
1203
1204 if (!rt6_is_gw_or_nonexthop(ort)) {
David Ahern93c2fb22018-04-18 15:38:59 -07001205 if (ort->fib6_dst.plen != 128 &&
1206 ipv6_addr_equal(&ort->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001207 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001209 if (rt->rt6i_src.plen && saddr) {
1210 rt->rt6i_src.addr = *saddr;
1211 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001212 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001213#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001216 return rt;
1217}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
David Ahern8d1c8022018-04-17 17:33:26 -07001219static struct rt6_info *ip6_rt_pcpu_alloc(struct fib6_info *rt)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001220{
David Ahern3b6761d2018-04-17 17:33:20 -07001221 unsigned short flags = fib6_info_dst_flags(rt);
David Ahern4832c302017-08-17 12:17:20 -07001222 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001223 struct rt6_info *pcpu_rt;
1224
Wei Wange873e4b2018-07-21 20:56:32 -07001225 if (!fib6_info_hold_safe(rt))
1226 return NULL;
1227
David Ahern4832c302017-08-17 12:17:20 -07001228 rcu_read_lock();
1229 dev = ip6_rt_get_dev_rcu(rt);
David Ahern93531c62018-04-17 17:33:25 -07001230 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001231 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001232 if (!pcpu_rt) {
1233 fib6_info_release(rt);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001234 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001235 }
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001236 ip6_rt_copy_init(pcpu_rt, rt);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001237 pcpu_rt->rt6i_flags |= RTF_PCPU;
1238 return pcpu_rt;
1239}
1240
Wei Wang66f5d6c2017-10-06 12:06:10 -07001241/* It should be called with rcu_read_lock() acquired */
David Ahern8d1c8022018-04-17 17:33:26 -07001242static struct rt6_info *rt6_get_pcpu_route(struct fib6_info *rt)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001243{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001244 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001245
1246 p = this_cpu_ptr(rt->rt6i_pcpu);
1247 pcpu_rt = *p;
1248
David Ahernd4ead6b2018-04-17 17:33:16 -07001249 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001250 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001251
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001252 return pcpu_rt;
1253}
1254
David Ahernafb1d4b52018-04-17 17:33:11 -07001255static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Ahern8d1c8022018-04-17 17:33:26 -07001256 struct fib6_info *rt)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001257{
1258 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001259
1260 pcpu_rt = ip6_rt_pcpu_alloc(rt);
1261 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001262 dst_hold(&net->ipv6.ip6_null_entry->dst);
1263 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001264 }
1265
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001266 dst_hold(&pcpu_rt->dst);
Wei Wanga94b9362017-10-06 12:06:04 -07001267 p = this_cpu_ptr(rt->rt6i_pcpu);
1268 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001269 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001270
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001271 return pcpu_rt;
1272}
1273
Wei Wang35732d02017-10-06 12:05:57 -07001274/* exception hash table implementation
1275 */
1276static DEFINE_SPINLOCK(rt6_exception_lock);
1277
1278/* Remove rt6_ex from hash table and free the memory
1279 * Caller must hold rt6_exception_lock
1280 */
1281static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1282 struct rt6_exception *rt6_ex)
1283{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001284 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001285 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001286
Wei Wang35732d02017-10-06 12:05:57 -07001287 if (!bucket || !rt6_ex)
1288 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001289
1290 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001291 net->ipv6.rt6_stats->fib_rt_cache--;
1292
1293 /* purge completely the exception to allow releasing the held resources:
1294 * some [sk] cache may keep the dst around for unlimited time
1295 */
1296 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1297 lockdep_is_held(&rt6_exception_lock));
1298 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1299 fib6_info_release(from);
1300 dst_dev_put(&rt6_ex->rt6i->dst);
1301
Wei Wang35732d02017-10-06 12:05:57 -07001302 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001303 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001304 kfree_rcu(rt6_ex, rcu);
1305 WARN_ON_ONCE(!bucket->depth);
1306 bucket->depth--;
1307}
1308
1309/* Remove oldest rt6_ex in bucket and free the memory
1310 * Caller must hold rt6_exception_lock
1311 */
1312static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1313{
1314 struct rt6_exception *rt6_ex, *oldest = NULL;
1315
1316 if (!bucket)
1317 return;
1318
1319 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1320 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1321 oldest = rt6_ex;
1322 }
1323 rt6_remove_exception(bucket, oldest);
1324}
1325
1326static u32 rt6_exception_hash(const struct in6_addr *dst,
1327 const struct in6_addr *src)
1328{
1329 static u32 seed __read_mostly;
1330 u32 val;
1331
1332 net_get_random_once(&seed, sizeof(seed));
1333 val = jhash(dst, sizeof(*dst), seed);
1334
1335#ifdef CONFIG_IPV6_SUBTREES
1336 if (src)
1337 val = jhash(src, sizeof(*src), val);
1338#endif
1339 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1340}
1341
1342/* Helper function to find the cached rt in the hash table
1343 * and update bucket pointer to point to the bucket for this
1344 * (daddr, saddr) pair
1345 * Caller must hold rt6_exception_lock
1346 */
1347static struct rt6_exception *
1348__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1349 const struct in6_addr *daddr,
1350 const struct in6_addr *saddr)
1351{
1352 struct rt6_exception *rt6_ex;
1353 u32 hval;
1354
1355 if (!(*bucket) || !daddr)
1356 return NULL;
1357
1358 hval = rt6_exception_hash(daddr, saddr);
1359 *bucket += hval;
1360
1361 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1362 struct rt6_info *rt6 = rt6_ex->rt6i;
1363 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1364
1365#ifdef CONFIG_IPV6_SUBTREES
1366 if (matched && saddr)
1367 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1368#endif
1369 if (matched)
1370 return rt6_ex;
1371 }
1372 return NULL;
1373}
1374
1375/* Helper function to find the cached rt in the hash table
1376 * and update bucket pointer to point to the bucket for this
1377 * (daddr, saddr) pair
1378 * Caller must hold rcu_read_lock()
1379 */
1380static struct rt6_exception *
1381__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1382 const struct in6_addr *daddr,
1383 const struct in6_addr *saddr)
1384{
1385 struct rt6_exception *rt6_ex;
1386 u32 hval;
1387
1388 WARN_ON_ONCE(!rcu_read_lock_held());
1389
1390 if (!(*bucket) || !daddr)
1391 return NULL;
1392
1393 hval = rt6_exception_hash(daddr, saddr);
1394 *bucket += hval;
1395
1396 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1397 struct rt6_info *rt6 = rt6_ex->rt6i;
1398 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1399
1400#ifdef CONFIG_IPV6_SUBTREES
1401 if (matched && saddr)
1402 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1403#endif
1404 if (matched)
1405 return rt6_ex;
1406 }
1407 return NULL;
1408}
1409
David Ahern8d1c8022018-04-17 17:33:26 -07001410static unsigned int fib6_mtu(const struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001411{
David Ahernd4ead6b2018-04-17 17:33:16 -07001412 unsigned int mtu;
1413
David Aherndcd1f572018-04-18 15:39:05 -07001414 if (rt->fib6_pmtu) {
1415 mtu = rt->fib6_pmtu;
1416 } else {
1417 struct net_device *dev = fib6_info_nh_dev(rt);
1418 struct inet6_dev *idev;
1419
1420 rcu_read_lock();
1421 idev = __in6_dev_get(dev);
1422 mtu = idev->cnf.mtu6;
1423 rcu_read_unlock();
1424 }
1425
David Ahernd4ead6b2018-04-17 17:33:16 -07001426 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1427
David Ahernad1601a2019-03-27 20:53:56 -07001428 return mtu - lwtunnel_headroom(rt->fib6_nh.fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001429}
1430
Wei Wang35732d02017-10-06 12:05:57 -07001431static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern8d1c8022018-04-17 17:33:26 -07001432 struct fib6_info *ort)
Wei Wang35732d02017-10-06 12:05:57 -07001433{
David Ahern5e670d82018-04-17 17:33:14 -07001434 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001435 struct rt6_exception_bucket *bucket;
1436 struct in6_addr *src_key = NULL;
1437 struct rt6_exception *rt6_ex;
1438 int err = 0;
1439
Wei Wang35732d02017-10-06 12:05:57 -07001440 spin_lock_bh(&rt6_exception_lock);
1441
1442 if (ort->exception_bucket_flushed) {
1443 err = -EINVAL;
1444 goto out;
1445 }
1446
1447 bucket = rcu_dereference_protected(ort->rt6i_exception_bucket,
1448 lockdep_is_held(&rt6_exception_lock));
1449 if (!bucket) {
1450 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1451 GFP_ATOMIC);
1452 if (!bucket) {
1453 err = -ENOMEM;
1454 goto out;
1455 }
1456 rcu_assign_pointer(ort->rt6i_exception_bucket, bucket);
1457 }
1458
1459#ifdef CONFIG_IPV6_SUBTREES
1460 /* rt6i_src.plen != 0 indicates ort is in subtree
1461 * and exception table is indexed by a hash of
1462 * both rt6i_dst and rt6i_src.
1463 * Otherwise, the exception table is indexed by
1464 * a hash of only rt6i_dst.
1465 */
David Ahern93c2fb22018-04-18 15:38:59 -07001466 if (ort->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001467 src_key = &nrt->rt6i_src.addr;
1468#endif
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001469 /* rt6_mtu_change() might lower mtu on ort.
1470 * Only insert this exception route if its mtu
1471 * is less than ort's mtu value.
1472 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001473 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001474 err = -EINVAL;
1475 goto out;
1476 }
Wei Wang60006a42017-10-06 12:05:58 -07001477
Wei Wang35732d02017-10-06 12:05:57 -07001478 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1479 src_key);
1480 if (rt6_ex)
1481 rt6_remove_exception(bucket, rt6_ex);
1482
1483 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1484 if (!rt6_ex) {
1485 err = -ENOMEM;
1486 goto out;
1487 }
1488 rt6_ex->rt6i = nrt;
1489 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001490 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1491 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001492 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001493
1494 if (bucket->depth > FIB6_MAX_DEPTH)
1495 rt6_exception_remove_oldest(bucket);
1496
1497out:
1498 spin_unlock_bh(&rt6_exception_lock);
1499
1500 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001501 if (!err) {
David Ahern93c2fb22018-04-18 15:38:59 -07001502 spin_lock_bh(&ort->fib6_table->tb6_lock);
David Ahern7aef6852018-04-17 17:33:10 -07001503 fib6_update_sernum(net, ort);
David Ahern93c2fb22018-04-18 15:38:59 -07001504 spin_unlock_bh(&ort->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001505 fib6_force_start_gc(net);
1506 }
Wei Wang35732d02017-10-06 12:05:57 -07001507
1508 return err;
1509}
1510
David Ahern8d1c8022018-04-17 17:33:26 -07001511void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001512{
1513 struct rt6_exception_bucket *bucket;
1514 struct rt6_exception *rt6_ex;
1515 struct hlist_node *tmp;
1516 int i;
1517
1518 spin_lock_bh(&rt6_exception_lock);
1519 /* Prevent rt6_insert_exception() to recreate the bucket list */
1520 rt->exception_bucket_flushed = 1;
1521
1522 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1523 lockdep_is_held(&rt6_exception_lock));
1524 if (!bucket)
1525 goto out;
1526
1527 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1528 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1529 rt6_remove_exception(bucket, rt6_ex);
1530 WARN_ON_ONCE(bucket->depth);
1531 bucket++;
1532 }
1533
1534out:
1535 spin_unlock_bh(&rt6_exception_lock);
1536}
1537
1538/* Find cached rt in the hash table inside passed in rt
1539 * Caller has to hold rcu_read_lock()
1540 */
David Ahern8d1c8022018-04-17 17:33:26 -07001541static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt,
Wei Wang35732d02017-10-06 12:05:57 -07001542 struct in6_addr *daddr,
1543 struct in6_addr *saddr)
1544{
1545 struct rt6_exception_bucket *bucket;
1546 struct in6_addr *src_key = NULL;
1547 struct rt6_exception *rt6_ex;
1548 struct rt6_info *res = NULL;
1549
1550 bucket = rcu_dereference(rt->rt6i_exception_bucket);
1551
1552#ifdef CONFIG_IPV6_SUBTREES
1553 /* rt6i_src.plen != 0 indicates rt is in subtree
1554 * and exception table is indexed by a hash of
1555 * both rt6i_dst and rt6i_src.
1556 * Otherwise, the exception table is indexed by
1557 * a hash of only rt6i_dst.
1558 */
David Ahern93c2fb22018-04-18 15:38:59 -07001559 if (rt->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001560 src_key = saddr;
1561#endif
1562 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1563
1564 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
1565 res = rt6_ex->rt6i;
1566
1567 return res;
1568}
1569
1570/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001571static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001572{
Wei Wang35732d02017-10-06 12:05:57 -07001573 struct rt6_exception_bucket *bucket;
1574 struct in6_addr *src_key = NULL;
1575 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001576 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001577 int err;
1578
Eric Dumazet091311d2018-04-24 09:22:49 -07001579 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001580 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001581 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001582 return -EINVAL;
1583
1584 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1585 return -ENOENT;
1586
1587 spin_lock_bh(&rt6_exception_lock);
1588 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1589 lockdep_is_held(&rt6_exception_lock));
1590#ifdef CONFIG_IPV6_SUBTREES
1591 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1592 * and exception table is indexed by a hash of
1593 * both rt6i_dst and rt6i_src.
1594 * Otherwise, the exception table is indexed by
1595 * a hash of only rt6i_dst.
1596 */
David Ahern93c2fb22018-04-18 15:38:59 -07001597 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001598 src_key = &rt->rt6i_src.addr;
1599#endif
1600 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1601 &rt->rt6i_dst.addr,
1602 src_key);
1603 if (rt6_ex) {
1604 rt6_remove_exception(bucket, rt6_ex);
1605 err = 0;
1606 } else {
1607 err = -ENOENT;
1608 }
1609
1610 spin_unlock_bh(&rt6_exception_lock);
1611 return err;
1612}
1613
1614/* Find rt6_ex which contains the passed in rt cache and
1615 * refresh its stamp
1616 */
1617static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1618{
Wei Wang35732d02017-10-06 12:05:57 -07001619 struct rt6_exception_bucket *bucket;
1620 struct in6_addr *src_key = NULL;
1621 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001622 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001623
1624 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001625 from = rcu_dereference(rt->from);
1626 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1627 goto unlock;
1628
Wei Wang35732d02017-10-06 12:05:57 -07001629 bucket = rcu_dereference(from->rt6i_exception_bucket);
1630
1631#ifdef CONFIG_IPV6_SUBTREES
1632 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1633 * and exception table is indexed by a hash of
1634 * both rt6i_dst and rt6i_src.
1635 * Otherwise, the exception table is indexed by
1636 * a hash of only rt6i_dst.
1637 */
David Ahern93c2fb22018-04-18 15:38:59 -07001638 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001639 src_key = &rt->rt6i_src.addr;
1640#endif
1641 rt6_ex = __rt6_find_exception_rcu(&bucket,
1642 &rt->rt6i_dst.addr,
1643 src_key);
1644 if (rt6_ex)
1645 rt6_ex->stamp = jiffies;
1646
Paolo Abeni193f3682019-02-21 11:19:41 +01001647unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001648 rcu_read_unlock();
1649}
1650
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001651static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1652 struct rt6_info *rt, int mtu)
1653{
1654 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1655 * lowest MTU in the path: always allow updating the route PMTU to
1656 * reflect PMTU decreases.
1657 *
1658 * If the new MTU is higher, and the route PMTU is equal to the local
1659 * MTU, this means the old MTU is the lowest in the path, so allow
1660 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1661 * handle this.
1662 */
1663
1664 if (dst_mtu(&rt->dst) >= mtu)
1665 return true;
1666
1667 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1668 return true;
1669
1670 return false;
1671}
1672
1673static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001674 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001675{
1676 struct rt6_exception_bucket *bucket;
1677 struct rt6_exception *rt6_ex;
1678 int i;
1679
1680 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1681 lockdep_is_held(&rt6_exception_lock));
1682
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001683 if (!bucket)
1684 return;
1685
1686 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1687 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1688 struct rt6_info *entry = rt6_ex->rt6i;
1689
1690 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001691 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001692 * been updated.
1693 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001694 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001695 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001696 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001697 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001698 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001699 }
1700}
1701
Wei Wangb16cb452017-10-06 12:06:00 -07001702#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1703
David Ahern8d1c8022018-04-17 17:33:26 -07001704static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001705 struct in6_addr *gateway)
1706{
1707 struct rt6_exception_bucket *bucket;
1708 struct rt6_exception *rt6_ex;
1709 struct hlist_node *tmp;
1710 int i;
1711
1712 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1713 return;
1714
1715 spin_lock_bh(&rt6_exception_lock);
1716 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1717 lockdep_is_held(&rt6_exception_lock));
1718
1719 if (bucket) {
1720 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1721 hlist_for_each_entry_safe(rt6_ex, tmp,
1722 &bucket->chain, hlist) {
1723 struct rt6_info *entry = rt6_ex->rt6i;
1724
1725 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1726 RTF_CACHE_GATEWAY &&
1727 ipv6_addr_equal(gateway,
1728 &entry->rt6i_gateway)) {
1729 rt6_remove_exception(bucket, rt6_ex);
1730 }
1731 }
1732 bucket++;
1733 }
1734 }
1735
1736 spin_unlock_bh(&rt6_exception_lock);
1737}
1738
Wei Wangc757faa2017-10-06 12:06:01 -07001739static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1740 struct rt6_exception *rt6_ex,
1741 struct fib6_gc_args *gc_args,
1742 unsigned long now)
1743{
1744 struct rt6_info *rt = rt6_ex->rt6i;
1745
Paolo Abeni1859bac2017-10-19 16:07:11 +02001746 /* we are pruning and obsoleting aged-out and non gateway exceptions
1747 * even if others have still references to them, so that on next
1748 * dst_check() such references can be dropped.
1749 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1750 * expired, independently from their aging, as per RFC 8201 section 4
1751 */
Wei Wang31afeb42018-01-26 11:40:17 -08001752 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1753 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1754 RT6_TRACE("aging clone %p\n", rt);
1755 rt6_remove_exception(bucket, rt6_ex);
1756 return;
1757 }
1758 } else if (time_after(jiffies, rt->dst.expires)) {
1759 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001760 rt6_remove_exception(bucket, rt6_ex);
1761 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001762 }
1763
1764 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001765 struct neighbour *neigh;
1766 __u8 neigh_flags = 0;
1767
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001768 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1769 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001770 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001771
Wei Wangc757faa2017-10-06 12:06:01 -07001772 if (!(neigh_flags & NTF_ROUTER)) {
1773 RT6_TRACE("purging route %p via non-router but gateway\n",
1774 rt);
1775 rt6_remove_exception(bucket, rt6_ex);
1776 return;
1777 }
1778 }
Wei Wang31afeb42018-01-26 11:40:17 -08001779
Wei Wangc757faa2017-10-06 12:06:01 -07001780 gc_args->more++;
1781}
1782
David Ahern8d1c8022018-04-17 17:33:26 -07001783void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001784 struct fib6_gc_args *gc_args,
1785 unsigned long now)
1786{
1787 struct rt6_exception_bucket *bucket;
1788 struct rt6_exception *rt6_ex;
1789 struct hlist_node *tmp;
1790 int i;
1791
1792 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1793 return;
1794
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001795 rcu_read_lock_bh();
1796 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001797 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1798 lockdep_is_held(&rt6_exception_lock));
1799
1800 if (bucket) {
1801 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1802 hlist_for_each_entry_safe(rt6_ex, tmp,
1803 &bucket->chain, hlist) {
1804 rt6_age_examine_exception(bucket, rt6_ex,
1805 gc_args, now);
1806 }
1807 bucket++;
1808 }
1809 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001810 spin_unlock(&rt6_exception_lock);
1811 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001812}
1813
David Ahern1d053da2018-05-09 20:34:21 -07001814/* must be called with rcu lock held */
1815struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
1816 int oif, struct flowi6 *fl6, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001818 struct fib6_node *fn, *saved_fn;
David Ahern8d1c8022018-04-17 17:33:26 -07001819 struct fib6_info *f6i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
David Ahern64547432018-05-09 20:34:19 -07001821 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001822 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823
David Ahernca254492015-10-12 11:47:10 -07001824 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1825 oif = 0;
1826
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001827redo_rt6_select:
David Ahern23fb93a2018-04-17 17:33:23 -07001828 f6i = rt6_select(net, fn, oif, strict);
David Ahern23fb93a2018-04-17 17:33:23 -07001829 if (f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001830 fn = fib6_backtrack(fn, &fl6->saddr);
1831 if (fn)
1832 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001833 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1834 /* also consider unreachable route */
1835 strict &= ~RT6_LOOKUP_F_REACHABLE;
1836 fn = saved_fn;
1837 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001838 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001839 }
1840
David Ahernd4bea422018-05-09 20:34:24 -07001841 trace_fib6_table_lookup(net, f6i, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001842
David Ahern1d053da2018-05-09 20:34:21 -07001843 return f6i;
1844}
1845
1846struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1847 int oif, struct flowi6 *fl6,
1848 const struct sk_buff *skb, int flags)
1849{
David Ahernb1d40992019-04-16 14:35:59 -07001850 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001851 struct rt6_info *rt;
1852 int strict = 0;
1853
1854 strict |= flags & RT6_LOOKUP_F_IFACE;
1855 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1856 if (net->ipv6.devconf_all->forwarding == 0)
1857 strict |= RT6_LOOKUP_F_REACHABLE;
1858
1859 rcu_read_lock();
1860
David Ahernb1d40992019-04-16 14:35:59 -07001861 res.f6i = fib6_table_lookup(net, table, oif, fl6, strict);
1862 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001863 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001864 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001865 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001866 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001867 }
1868
David Ahernb1d40992019-04-16 14:35:59 -07001869 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001870
David Ahern23fb93a2018-04-17 17:33:23 -07001871 /*Search through exception table */
David Ahernb1d40992019-04-16 14:35:59 -07001872 rt = rt6_find_cached_rt(res.f6i, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001873 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001874 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001875 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001876
Wei Wang66f5d6c2017-10-06 12:06:10 -07001877 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001878 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001879 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001880 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001881 /* Create a RTF_CACHE clone which will not be
1882 * owned by the fib6 tree. It is for the special case where
1883 * the daddr in the skb during the neighbor look-up is different
1884 * from the fl6->daddr used to look-up route here.
1885 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001886 struct rt6_info *uncached_rt;
1887
David Ahernb1d40992019-04-16 14:35:59 -07001888 uncached_rt = ip6_rt_cache_alloc(res.f6i, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001889
David Ahern4d85cd02018-04-20 15:37:59 -07001890 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001891
Wei Wang1cfb71e2017-06-17 10:42:33 -07001892 if (uncached_rt) {
1893 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1894 * No need for another dst_hold()
1895 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001896 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001897 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001898 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001899 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001900 dst_hold(&uncached_rt->dst);
1901 }
David Ahernb8115802015-11-19 12:24:22 -08001902
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001903 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001904 } else {
1905 /* Get a percpu copy */
1906
1907 struct rt6_info *pcpu_rt;
1908
Eric Dumazet951f7882017-10-08 21:07:18 -07001909 local_bh_disable();
David Ahernb1d40992019-04-16 14:35:59 -07001910 pcpu_rt = rt6_get_pcpu_route(res.f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001911
David Ahern93531c62018-04-17 17:33:25 -07001912 if (!pcpu_rt)
David Ahernb1d40992019-04-16 14:35:59 -07001913 pcpu_rt = rt6_make_pcpu_route(net, res.f6i);
David Ahern93531c62018-04-17 17:33:25 -07001914
Eric Dumazet951f7882017-10-08 21:07:18 -07001915 local_bh_enable();
1916 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001917
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001918 return pcpu_rt;
1919 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001920}
David Ahern9ff74382016-06-13 13:44:19 -07001921EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001922
David Ahernb75cc8f2018-03-02 08:32:17 -08001923static struct rt6_info *ip6_pol_route_input(struct net *net,
1924 struct fib6_table *table,
1925 struct flowi6 *fl6,
1926 const struct sk_buff *skb,
1927 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001928{
David Ahernb75cc8f2018-03-02 08:32:17 -08001929 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001930}
1931
Mahesh Bandeward409b842016-09-16 12:59:08 -07001932struct dst_entry *ip6_route_input_lookup(struct net *net,
1933 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001934 struct flowi6 *fl6,
1935 const struct sk_buff *skb,
1936 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001937{
1938 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1939 flags |= RT6_LOOKUP_F_IFACE;
1940
David Ahernb75cc8f2018-03-02 08:32:17 -08001941 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001942}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001943EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001944
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001945static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001946 struct flow_keys *keys,
1947 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001948{
1949 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1950 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001951 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001952 const struct ipv6hdr *inner_iph;
1953 const struct icmp6hdr *icmph;
1954 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001955 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001956
1957 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1958 goto out;
1959
Eric Dumazetcea67a22018-04-29 09:54:59 -07001960 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1961 sizeof(_icmph), &_icmph);
1962 if (!icmph)
1963 goto out;
1964
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001965 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1966 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1967 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1968 icmph->icmp6_type != ICMPV6_PARAMPROB)
1969 goto out;
1970
1971 inner_iph = skb_header_pointer(skb,
1972 skb_transport_offset(skb) + sizeof(*icmph),
1973 sizeof(_inner_iph), &_inner_iph);
1974 if (!inner_iph)
1975 goto out;
1976
1977 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001978 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001979out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001980 if (_flkeys) {
1981 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
1982 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
1983 keys->tags.flow_label = _flkeys->tags.flow_label;
1984 keys->basic.ip_proto = _flkeys->basic.ip_proto;
1985 } else {
1986 keys->addrs.v6addrs.src = key_iph->saddr;
1987 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02001988 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001989 keys->basic.ip_proto = key_iph->nexthdr;
1990 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001991}
1992
1993/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08001994u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
1995 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001996{
1997 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08001998 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001999
David S. Millerbbfa0472018-03-12 11:09:33 -04002000 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002001 case 0:
2002 memset(&hash_keys, 0, sizeof(hash_keys));
2003 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2004 if (skb) {
2005 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2006 } else {
2007 hash_keys.addrs.v6addrs.src = fl6->saddr;
2008 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002009 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002010 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2011 }
2012 break;
2013 case 1:
2014 if (skb) {
2015 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2016 struct flow_keys keys;
2017
2018 /* short-circuit if we already have L4 hash present */
2019 if (skb->l4_hash)
2020 return skb_get_hash_raw(skb) >> 1;
2021
2022 memset(&hash_keys, 0, sizeof(hash_keys));
2023
2024 if (!flkeys) {
2025 skb_flow_dissect_flow_keys(skb, &keys, flag);
2026 flkeys = &keys;
2027 }
2028 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2029 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2030 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2031 hash_keys.ports.src = flkeys->ports.src;
2032 hash_keys.ports.dst = flkeys->ports.dst;
2033 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2034 } else {
2035 memset(&hash_keys, 0, sizeof(hash_keys));
2036 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2037 hash_keys.addrs.v6addrs.src = fl6->saddr;
2038 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2039 hash_keys.ports.src = fl6->fl6_sport;
2040 hash_keys.ports.dst = fl6->fl6_dport;
2041 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2042 }
2043 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002044 }
David Ahern9a2a5372018-03-02 08:32:15 -08002045 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002046
David Ahern9a2a5372018-03-02 08:32:15 -08002047 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002048}
2049
Thomas Grafc71099a2006-08-04 23:20:06 -07002050void ip6_route_input(struct sk_buff *skb)
2051{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002052 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002053 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002054 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002055 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002056 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002057 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002058 .daddr = iph->daddr,
2059 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002060 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002061 .flowi6_mark = skb->mark,
2062 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002063 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002064 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002065
Jiri Benc904af042015-08-20 13:56:31 +02002066 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002067 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002068 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002069
2070 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2071 flkeys = &_flkeys;
2072
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002073 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002074 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002075 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002076 skb_dst_set(skb,
2077 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002078}
2079
David Ahernb75cc8f2018-03-02 08:32:17 -08002080static struct rt6_info *ip6_pol_route_output(struct net *net,
2081 struct fib6_table *table,
2082 struct flowi6 *fl6,
2083 const struct sk_buff *skb,
2084 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002085{
David Ahernb75cc8f2018-03-02 08:32:17 -08002086 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002087}
2088
Paolo Abeni6f21c962016-01-29 12:30:19 +01002089struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2090 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002091{
David Ahernd46a9d62015-10-21 08:42:22 -07002092 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002093
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002094 if (ipv6_addr_type(&fl6->daddr) &
2095 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002096 struct dst_entry *dst;
2097
2098 dst = l3mdev_link_scope_lookup(net, fl6);
2099 if (dst)
2100 return dst;
2101 }
David Ahernca254492015-10-12 11:47:10 -07002102
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002103 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002104
David Ahernd46a9d62015-10-21 08:42:22 -07002105 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002106 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002107 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002108 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002109
David Ahernd46a9d62015-10-21 08:42:22 -07002110 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002111 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002112 else if (sk)
2113 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002114
David Ahernb75cc8f2018-03-02 08:32:17 -08002115 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002117EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
David S. Miller2774c132011-03-01 14:59:04 -08002119struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002120{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002121 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002122 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002123 struct dst_entry *new = NULL;
2124
Wei Wang1dbe32522017-06-17 10:42:26 -07002125 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002126 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002127 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002128 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002129 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002130
Changli Gaod8d1f302010-06-10 23:31:35 -07002131 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002132 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002133 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002134 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002135
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002136 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002137
Wei Wang1dbe32522017-06-17 10:42:26 -07002138 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002139 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002140 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002141
2142 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2143#ifdef CONFIG_IPV6_SUBTREES
2144 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2145#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002146 }
2147
David S. Miller69ead7a2011-03-01 14:45:33 -08002148 dst_release(dst_orig);
2149 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002150}
David S. Miller14e50e52007-05-24 18:17:54 -07002151
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152/*
2153 * Destination cache support functions
2154 */
2155
David Ahern8d1c8022018-04-17 17:33:26 -07002156static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002157{
Steffen Klassert36143642017-08-25 09:05:42 +02002158 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002159
David Ahern8ae86972018-04-20 15:38:03 -07002160 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002161 return false;
2162
2163 if (fib6_check_expired(f6i))
2164 return false;
2165
2166 return true;
2167}
2168
David Aherna68886a2018-04-20 15:38:02 -07002169static struct dst_entry *rt6_check(struct rt6_info *rt,
2170 struct fib6_info *from,
2171 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002172{
Wei Wangc5cff852017-08-21 09:47:10 -07002173 u32 rt_cookie = 0;
2174
David Aherna68886a2018-04-20 15:38:02 -07002175 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002176 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002177 return NULL;
2178
2179 if (rt6_check_expired(rt))
2180 return NULL;
2181
2182 return &rt->dst;
2183}
2184
David Aherna68886a2018-04-20 15:38:02 -07002185static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2186 struct fib6_info *from,
2187 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002188{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002189 if (!__rt6_check_expired(rt) &&
2190 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002191 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002192 return &rt->dst;
2193 else
2194 return NULL;
2195}
2196
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2198{
David Aherna87b7dc2018-04-20 15:38:00 -07002199 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002200 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 struct rt6_info *rt;
2202
David Aherna87b7dc2018-04-20 15:38:00 -07002203 rt = container_of(dst, struct rt6_info, dst);
2204
2205 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002207 /* All IPV6 dsts are created with ->obsolete set to the value
2208 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2209 * into this function always.
2210 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002211
David Aherna68886a2018-04-20 15:38:02 -07002212 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002213
David Aherna68886a2018-04-20 15:38:02 -07002214 if (from && (rt->rt6i_flags & RTF_PCPU ||
2215 unlikely(!list_empty(&rt->rt6i_uncached))))
2216 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002217 else
David Aherna68886a2018-04-20 15:38:02 -07002218 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002219
2220 rcu_read_unlock();
2221
2222 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223}
2224
2225static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2226{
2227 struct rt6_info *rt = (struct rt6_info *) dst;
2228
2229 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002230 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002231 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002232 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002233 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002234 dst = NULL;
2235 }
David Ahernc3c14da2018-04-23 11:32:06 -07002236 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002237 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002239 dst = NULL;
2240 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002242 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243}
2244
2245static void ip6_link_failure(struct sk_buff *skb)
2246{
2247 struct rt6_info *rt;
2248
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002249 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
Eric Dumazetadf30902009-06-02 05:19:30 +00002251 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002253 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002254 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002255 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002256 } else {
David Aherna68886a2018-04-20 15:38:02 -07002257 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002258 struct fib6_node *fn;
2259
David Aherna68886a2018-04-20 15:38:02 -07002260 from = rcu_dereference(rt->from);
2261 if (from) {
2262 fn = rcu_dereference(from->fib6_node);
2263 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2264 fn->fn_sernum = -1;
2265 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002266 }
David Ahern8a14e462018-04-23 11:32:07 -07002267 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 }
2269}
2270
David Ahern6a3e0302018-04-20 15:37:57 -07002271static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2272{
David Aherna68886a2018-04-20 15:38:02 -07002273 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2274 struct fib6_info *from;
2275
2276 rcu_read_lock();
2277 from = rcu_dereference(rt0->from);
2278 if (from)
2279 rt0->dst.expires = from->expires;
2280 rcu_read_unlock();
2281 }
David Ahern6a3e0302018-04-20 15:37:57 -07002282
2283 dst_set_expires(&rt0->dst, timeout);
2284 rt0->rt6i_flags |= RTF_EXPIRES;
2285}
2286
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002287static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2288{
2289 struct net *net = dev_net(rt->dst.dev);
2290
David Ahernd4ead6b2018-04-17 17:33:16 -07002291 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002292 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002293 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2294}
2295
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002296static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2297{
2298 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002299 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002300}
2301
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002302static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2303 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002305 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002306 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307
Xin Long19bda362016-10-28 18:18:01 +08002308 if (dst_metric_locked(dst, RTAX_MTU))
2309 return;
2310
Julian Anastasov0dec8792017-02-06 23:14:16 +02002311 if (iph) {
2312 daddr = &iph->daddr;
2313 saddr = &iph->saddr;
2314 } else if (sk) {
2315 daddr = &sk->sk_v6_daddr;
2316 saddr = &inet6_sk(sk)->saddr;
2317 } else {
2318 daddr = NULL;
2319 saddr = NULL;
2320 }
2321 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002322 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2323 if (mtu >= dst_mtu(dst))
2324 return;
David S. Miller81aded22012-06-15 14:54:11 -07002325
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002326 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002327 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002328 /* update rt6_ex->stamp for cache */
2329 if (rt6->rt6i_flags & RTF_CACHE)
2330 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002331 } else if (daddr) {
David Aherna68886a2018-04-20 15:38:02 -07002332 struct fib6_info *from;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002333 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002334
David Ahern4d85cd02018-04-20 15:37:59 -07002335 rcu_read_lock();
David Aherna68886a2018-04-20 15:38:02 -07002336 from = rcu_dereference(rt6->from);
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002337 if (!from) {
2338 rcu_read_unlock();
2339 return;
2340 }
David Aherna68886a2018-04-20 15:38:02 -07002341 nrt6 = ip6_rt_cache_alloc(from, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002342 if (nrt6) {
2343 rt6_do_update_pmtu(nrt6, mtu);
David Aherna68886a2018-04-20 15:38:02 -07002344 if (rt6_insert_exception(nrt6, from))
Wei Wang2b760fc2017-10-06 12:06:03 -07002345 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002346 }
David Aherna68886a2018-04-20 15:38:02 -07002347 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 }
2349}
2350
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002351static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2352 struct sk_buff *skb, u32 mtu)
2353{
2354 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2355}
2356
David S. Miller42ae66c2012-06-15 20:01:57 -07002357void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002358 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002359{
2360 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2361 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002362 struct flowi6 fl6 = {
2363 .flowi6_oif = oif,
2364 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2365 .daddr = iph->daddr,
2366 .saddr = iph->saddr,
2367 .flowlabel = ip6_flowinfo(iph),
2368 .flowi6_uid = uid,
2369 };
David S. Miller81aded22012-06-15 14:54:11 -07002370
2371 dst = ip6_route_output(net, NULL, &fl6);
2372 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002373 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002374 dst_release(dst);
2375}
2376EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2377
2378void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2379{
David Ahern7ddacfa2018-11-18 10:45:30 -08002380 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002381 struct dst_entry *dst;
2382
David Ahern7ddacfa2018-11-18 10:45:30 -08002383 if (!oif && skb->dev)
2384 oif = l3mdev_master_ifindex(skb->dev);
2385
2386 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002387
2388 dst = __sk_dst_get(sk);
2389 if (!dst || !dst->obsolete ||
2390 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2391 return;
2392
2393 bh_lock_sock(sk);
2394 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2395 ip6_datagram_dst_update(sk, false);
2396 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002397}
2398EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2399
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002400void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2401 const struct flowi6 *fl6)
2402{
2403#ifdef CONFIG_IPV6_SUBTREES
2404 struct ipv6_pinfo *np = inet6_sk(sk);
2405#endif
2406
2407 ip6_dst_store(sk, dst,
2408 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2409 &sk->sk_v6_daddr : NULL,
2410#ifdef CONFIG_IPV6_SUBTREES
2411 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2412 &np->saddr :
2413#endif
2414 NULL);
2415}
2416
David Ahern0b34eb02019-04-09 14:41:19 -07002417static bool ip6_redirect_nh_match(struct fib6_info *f6i,
2418 struct fib6_nh *nh,
2419 struct flowi6 *fl6,
2420 const struct in6_addr *gw,
2421 struct rt6_info **ret)
2422{
2423 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2424 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2425 return false;
2426
2427 /* rt_cache's gateway might be different from its 'parent'
2428 * in the case of an ip redirect.
2429 * So we keep searching in the exception table if the gateway
2430 * is different.
2431 */
2432 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2433 struct rt6_info *rt_cache;
2434
2435 rt_cache = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr);
2436 if (rt_cache &&
2437 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2438 *ret = rt_cache;
2439 return true;
2440 }
2441 return false;
2442 }
2443 return true;
2444}
2445
Duan Jiongb55b76b2013-09-04 19:44:21 +08002446/* Handle redirects */
2447struct ip6rd_flowi {
2448 struct flowi6 fl6;
2449 struct in6_addr gateway;
2450};
2451
2452static struct rt6_info *__ip6_route_redirect(struct net *net,
2453 struct fib6_table *table,
2454 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002455 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002456 int flags)
2457{
2458 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002459 struct rt6_info *ret = NULL;
David Ahern8d1c8022018-04-17 17:33:26 -07002460 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002461 struct fib6_node *fn;
2462
2463 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002464 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002465 *
2466 * RFC 4861 specifies that redirects should only be
2467 * accepted if they come from the nexthop to the target.
2468 * Due to the way the routes are chosen, this notion
2469 * is a bit fuzzy and one might need to check all possible
2470 * routes.
2471 */
2472
Wei Wang66f5d6c2017-10-06 12:06:10 -07002473 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002474 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002475restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002476 for_each_fib6_node_rt_rcu(fn) {
David Ahern14895682018-04-17 17:33:17 -07002477 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002478 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002479 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002480 break;
David Ahern0b34eb02019-04-09 14:41:19 -07002481 if (ip6_redirect_nh_match(rt, &rt->fib6_nh, fl6,
2482 &rdfl->gateway, &ret))
2483 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002484 }
2485
2486 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002487 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002488 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002489 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002490 goto out;
2491 }
2492
David Ahern421842e2018-04-17 17:33:18 -07002493 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002494 fn = fib6_backtrack(fn, &fl6->saddr);
2495 if (fn)
2496 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002497 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002498
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002499out:
David Ahern23fb93a2018-04-17 17:33:23 -07002500 if (ret)
David Ahern10585b42019-03-20 09:24:50 -07002501 ip6_hold_safe(net, &ret);
David Ahern23fb93a2018-04-17 17:33:23 -07002502 else
2503 ret = ip6_create_rt_rcu(rt);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002504
Wei Wang66f5d6c2017-10-06 12:06:10 -07002505 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002506
Paolo Abenib65f1642017-10-19 09:31:43 +02002507 trace_fib6_table_lookup(net, rt, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002508 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002509};
2510
2511static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002512 const struct flowi6 *fl6,
2513 const struct sk_buff *skb,
2514 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002515{
2516 int flags = RT6_LOOKUP_F_HAS_SADDR;
2517 struct ip6rd_flowi rdfl;
2518
2519 rdfl.fl6 = *fl6;
2520 rdfl.gateway = *gateway;
2521
David Ahernb75cc8f2018-03-02 08:32:17 -08002522 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002523 flags, __ip6_route_redirect);
2524}
2525
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002526void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2527 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002528{
2529 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2530 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002531 struct flowi6 fl6 = {
2532 .flowi6_iif = LOOPBACK_IFINDEX,
2533 .flowi6_oif = oif,
2534 .flowi6_mark = mark,
2535 .daddr = iph->daddr,
2536 .saddr = iph->saddr,
2537 .flowlabel = ip6_flowinfo(iph),
2538 .flowi6_uid = uid,
2539 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002540
David Ahernb75cc8f2018-03-02 08:32:17 -08002541 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002542 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002543 dst_release(dst);
2544}
2545EXPORT_SYMBOL_GPL(ip6_redirect);
2546
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002547void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002548{
2549 const struct ipv6hdr *iph = ipv6_hdr(skb);
2550 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2551 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002552 struct flowi6 fl6 = {
2553 .flowi6_iif = LOOPBACK_IFINDEX,
2554 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002555 .daddr = msg->dest,
2556 .saddr = iph->daddr,
2557 .flowi6_uid = sock_net_uid(net, NULL),
2558 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002559
David Ahernb75cc8f2018-03-02 08:32:17 -08002560 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002561 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002562 dst_release(dst);
2563}
2564
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002565void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2566{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002567 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2568 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002569}
2570EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2571
David S. Miller0dbaee32010-12-13 12:52:14 -08002572static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573{
David S. Miller0dbaee32010-12-13 12:52:14 -08002574 struct net_device *dev = dst->dev;
2575 unsigned int mtu = dst_mtu(dst);
2576 struct net *net = dev_net(dev);
2577
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2579
Daniel Lezcano55786892008-03-04 13:47:47 -08002580 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2581 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582
2583 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002584 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2585 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2586 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 * rely only on pmtu discovery"
2588 */
2589 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2590 mtu = IPV6_MAXPLEN;
2591 return mtu;
2592}
2593
Steffen Klassertebb762f2011-11-23 02:12:51 +00002594static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002595{
David S. Millerd33e4552010-12-14 13:01:14 -08002596 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002597 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002598
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002599 mtu = dst_metric_raw(dst, RTAX_MTU);
2600 if (mtu)
2601 goto out;
2602
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002603 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002604
2605 rcu_read_lock();
2606 idev = __in6_dev_get(dst->dev);
2607 if (idev)
2608 mtu = idev->cnf.mtu6;
2609 rcu_read_unlock();
2610
Eric Dumazet30f78d82014-04-10 21:23:36 -07002611out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002612 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2613
2614 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002615}
2616
David Ahern901731b2018-05-21 09:08:14 -07002617/* MTU selection:
2618 * 1. mtu on route is locked - use it
2619 * 2. mtu from nexthop exception
2620 * 3. mtu from egress device
2621 *
2622 * based on ip6_dst_mtu_forward and exception logic of
2623 * rt6_find_cached_rt; called with rcu_read_lock
2624 */
2625u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
2626 struct in6_addr *saddr)
2627{
2628 struct rt6_exception_bucket *bucket;
2629 struct rt6_exception *rt6_ex;
2630 struct in6_addr *src_key;
2631 struct inet6_dev *idev;
2632 u32 mtu = 0;
2633
2634 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2635 mtu = f6i->fib6_pmtu;
2636 if (mtu)
2637 goto out;
2638 }
2639
2640 src_key = NULL;
2641#ifdef CONFIG_IPV6_SUBTREES
2642 if (f6i->fib6_src.plen)
2643 src_key = saddr;
2644#endif
2645
2646 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2647 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2648 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2649 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2650
2651 if (likely(!mtu)) {
2652 struct net_device *dev = fib6_info_nh_dev(f6i);
2653
2654 mtu = IPV6_MIN_MTU;
2655 idev = __in6_dev_get(dev);
2656 if (idev && idev->cnf.mtu6 > mtu)
2657 mtu = idev->cnf.mtu6;
2658 }
2659
2660 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2661out:
2662 return mtu - lwtunnel_headroom(fib6_info_nh_lwt(f6i), mtu);
2663}
2664
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002665struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002666 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667{
David S. Miller87a11572011-12-06 17:04:13 -05002668 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 struct rt6_info *rt;
2670 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002671 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672
David S. Miller38308472011-12-03 18:02:47 -05002673 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002674 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
Martin KaFai Lauad706862015-08-14 11:05:52 -07002676 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002677 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002679 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 goto out;
2681 }
2682
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002683 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002684 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002685 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002686 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002687 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002688 rt->rt6i_dst.plen = 128;
2689 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002690 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691
Ido Schimmel4c981e22018-01-07 12:45:04 +02002692 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002693 * do proper release of the net_device
2694 */
2695 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002696 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697
David S. Miller87a11572011-12-06 17:04:13 -05002698 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2699
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700out:
David S. Miller87a11572011-12-06 17:04:13 -05002701 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702}
2703
Daniel Lezcano569d3642008-01-18 03:56:57 -08002704static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002706 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002707 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2708 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2709 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2710 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2711 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002712 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
Eric Dumazetfc66f952010-10-08 06:37:34 +00002714 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002715 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002716 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 goto out;
2718
Benjamin Thery6891a342008-03-04 13:49:47 -08002719 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002720 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002721 entries = dst_entries_get_slow(ops);
2722 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002723 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002725 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002726 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727}
2728
David Ahern8c145862016-04-24 21:26:04 -07002729static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2730 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002731 const struct in6_addr *gw_addr,
2732 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002733{
2734 struct flowi6 fl6 = {
2735 .flowi6_oif = cfg->fc_ifindex,
2736 .daddr = *gw_addr,
2737 .saddr = cfg->fc_prefsrc,
2738 };
2739 struct fib6_table *table;
2740 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002741
David Ahernf4797b32018-01-25 16:55:08 -08002742 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002743 if (!table)
2744 return NULL;
2745
2746 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2747 flags |= RT6_LOOKUP_F_HAS_SADDR;
2748
David Ahernf4797b32018-01-25 16:55:08 -08002749 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002750 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002751
2752 /* if table lookup failed, fall back to full lookup */
2753 if (rt == net->ipv6.ip6_null_entry) {
2754 ip6_rt_put(rt);
2755 rt = NULL;
2756 }
2757
2758 return rt;
2759}
2760
David Ahernfc1e64e2018-01-25 16:55:09 -08002761static int ip6_route_check_nh_onlink(struct net *net,
2762 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002763 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002764 struct netlink_ext_ack *extack)
2765{
David Ahern44750f82018-02-06 13:17:06 -08002766 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002767 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2768 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002769 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002770 struct rt6_info *grt;
2771 int err;
2772
2773 err = 0;
2774 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2775 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002776 rcu_read_lock();
2777 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002778 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002779 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002780 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002781 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002782 NL_SET_ERR_MSG(extack,
2783 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002784 err = -EINVAL;
2785 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002786 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002787
2788 ip6_rt_put(grt);
2789 }
2790
2791 return err;
2792}
2793
David Ahern1edce992018-01-25 16:55:07 -08002794static int ip6_route_check_nh(struct net *net,
2795 struct fib6_config *cfg,
2796 struct net_device **_dev,
2797 struct inet6_dev **idev)
2798{
2799 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2800 struct net_device *dev = _dev ? *_dev : NULL;
2801 struct rt6_info *grt = NULL;
2802 int err = -EHOSTUNREACH;
2803
2804 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002805 int flags = RT6_LOOKUP_F_IFACE;
2806
2807 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2808 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002809 if (grt) {
2810 if (grt->rt6i_flags & RTF_GATEWAY ||
2811 (dev && dev != grt->dst.dev)) {
2812 ip6_rt_put(grt);
2813 grt = NULL;
2814 }
2815 }
2816 }
2817
2818 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002819 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002820
2821 if (!grt)
2822 goto out;
2823
2824 if (dev) {
2825 if (dev != grt->dst.dev) {
2826 ip6_rt_put(grt);
2827 goto out;
2828 }
2829 } else {
2830 *_dev = dev = grt->dst.dev;
2831 *idev = grt->rt6i_idev;
2832 dev_hold(dev);
2833 in6_dev_hold(grt->rt6i_idev);
2834 }
2835
2836 if (!(grt->rt6i_flags & RTF_GATEWAY))
2837 err = 0;
2838
2839 ip6_rt_put(grt);
2840
2841out:
2842 return err;
2843}
2844
David Ahern9fbb7042018-03-13 08:29:36 -07002845static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2846 struct net_device **_dev, struct inet6_dev **idev,
2847 struct netlink_ext_ack *extack)
2848{
2849 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2850 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002851 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002852 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002853 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002854 int err = -EINVAL;
2855
2856 /* if gw_addr is local we will fail to detect this in case
2857 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2858 * will return already-added prefix route via interface that
2859 * prefix route was assigned to, which might be non-loopback.
2860 */
David Ahern232378e2018-03-13 08:29:37 -07002861 if (dev &&
2862 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2863 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002864 goto out;
2865 }
2866
2867 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2868 /* IPv6 strictly inhibits using not link-local
2869 * addresses as nexthop address.
2870 * Otherwise, router will not able to send redirects.
2871 * It is very good, but in some (rare!) circumstances
2872 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2873 * some exceptions. --ANK
2874 * We allow IPv4-mapped nexthops to support RFC4798-type
2875 * addressing
2876 */
2877 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2878 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2879 goto out;
2880 }
2881
2882 if (cfg->fc_flags & RTNH_F_ONLINK)
2883 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2884 else
2885 err = ip6_route_check_nh(net, cfg, _dev, idev);
2886
2887 if (err)
2888 goto out;
2889 }
2890
2891 /* reload in case device was changed */
2892 dev = *_dev;
2893
2894 err = -EINVAL;
2895 if (!dev) {
2896 NL_SET_ERR_MSG(extack, "Egress device not specified");
2897 goto out;
2898 } else if (dev->flags & IFF_LOOPBACK) {
2899 NL_SET_ERR_MSG(extack,
2900 "Egress device can not be loopback device for this route");
2901 goto out;
2902 }
David Ahern232378e2018-03-13 08:29:37 -07002903
2904 /* if we did not check gw_addr above, do so now that the
2905 * egress device has been resolved.
2906 */
2907 if (need_addr_check &&
2908 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2909 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2910 goto out;
2911 }
2912
David Ahern9fbb7042018-03-13 08:29:36 -07002913 err = 0;
2914out:
2915 return err;
2916}
2917
David Ahern83c442512019-03-27 20:53:50 -07002918static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2919{
2920 if ((flags & RTF_REJECT) ||
2921 (dev && (dev->flags & IFF_LOOPBACK) &&
2922 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2923 !(flags & RTF_LOCAL)))
2924 return true;
2925
2926 return false;
2927}
2928
2929int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2930 struct fib6_config *cfg, gfp_t gfp_flags,
2931 struct netlink_ext_ack *extack)
2932{
2933 struct net_device *dev = NULL;
2934 struct inet6_dev *idev = NULL;
2935 int addr_type;
2936 int err;
2937
David Ahernf1741732019-03-27 20:53:57 -07002938 fib6_nh->fib_nh_family = AF_INET6;
2939
David Ahern83c442512019-03-27 20:53:50 -07002940 err = -ENODEV;
2941 if (cfg->fc_ifindex) {
2942 dev = dev_get_by_index(net, cfg->fc_ifindex);
2943 if (!dev)
2944 goto out;
2945 idev = in6_dev_get(dev);
2946 if (!idev)
2947 goto out;
2948 }
2949
2950 if (cfg->fc_flags & RTNH_F_ONLINK) {
2951 if (!dev) {
2952 NL_SET_ERR_MSG(extack,
2953 "Nexthop device required for onlink");
2954 goto out;
2955 }
2956
2957 if (!(dev->flags & IFF_UP)) {
2958 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
2959 err = -ENETDOWN;
2960 goto out;
2961 }
2962
David Ahernad1601a2019-03-27 20:53:56 -07002963 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07002964 }
2965
David Ahernad1601a2019-03-27 20:53:56 -07002966 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07002967
2968 /* We cannot add true routes via loopback here,
2969 * they would result in kernel looping; promote them to reject routes
2970 */
2971 addr_type = ipv6_addr_type(&cfg->fc_dst);
2972 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
2973 /* hold loopback dev/idev if we haven't done so. */
2974 if (dev != net->loopback_dev) {
2975 if (dev) {
2976 dev_put(dev);
2977 in6_dev_put(idev);
2978 }
2979 dev = net->loopback_dev;
2980 dev_hold(dev);
2981 idev = in6_dev_get(dev);
2982 if (!idev) {
2983 err = -ENODEV;
2984 goto out;
2985 }
2986 }
2987 goto set_dev;
2988 }
2989
2990 if (cfg->fc_flags & RTF_GATEWAY) {
2991 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
2992 if (err)
2993 goto out;
2994
David Ahernad1601a2019-03-27 20:53:56 -07002995 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07002996 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07002997 }
2998
2999 err = -ENODEV;
3000 if (!dev)
3001 goto out;
3002
3003 if (idev->cnf.disable_ipv6) {
3004 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3005 err = -EACCES;
3006 goto out;
3007 }
3008
3009 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3010 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3011 err = -ENETDOWN;
3012 goto out;
3013 }
3014
3015 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3016 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003017 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003018
David Ahern979e2762019-03-27 20:53:58 -07003019 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3020 cfg->fc_encap_type, cfg, gfp_flags, extack);
3021 if (err)
3022 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003023set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003024 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003025 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003026 err = 0;
3027out:
3028 if (idev)
3029 in6_dev_put(idev);
3030
3031 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003032 lwtstate_put(fib6_nh->fib_nh_lws);
3033 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003034 if (dev)
3035 dev_put(dev);
3036 }
3037
3038 return err;
3039}
3040
David Aherndac7d0f2019-03-27 20:53:51 -07003041void fib6_nh_release(struct fib6_nh *fib6_nh)
3042{
David Ahern979e2762019-03-27 20:53:58 -07003043 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003044}
3045
David Ahern8d1c8022018-04-17 17:33:26 -07003046static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003047 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003048 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049{
Daniel Lezcano55786892008-03-04 13:47:47 -08003050 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003051 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003052 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003053 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003054 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055
David Ahern557c44b2017-04-19 14:19:43 -07003056 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003057 if (cfg->fc_flags & RTF_PCPU) {
3058 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003059 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003060 }
David Ahern557c44b2017-04-19 14:19:43 -07003061
Wei Wang2ea23522017-10-27 17:30:12 -07003062 /* RTF_CACHE is an internal flag; can not be set by userspace */
3063 if (cfg->fc_flags & RTF_CACHE) {
3064 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3065 goto out;
3066 }
3067
David Aherne8478e82018-04-17 17:33:13 -07003068 if (cfg->fc_type > RTN_MAX) {
3069 NL_SET_ERR_MSG(extack, "Invalid route type");
3070 goto out;
3071 }
3072
David Ahernd5d531c2017-05-21 10:12:05 -06003073 if (cfg->fc_dst_len > 128) {
3074 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003075 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003076 }
3077 if (cfg->fc_src_len > 128) {
3078 NL_SET_ERR_MSG(extack, "Invalid source address length");
3079 goto out;
3080 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003081#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003082 if (cfg->fc_src_len) {
3083 NL_SET_ERR_MSG(extack,
3084 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003085 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003086 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003088
Matti Vaittinend71314b2011-11-14 00:14:49 +00003089 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003090 if (cfg->fc_nlinfo.nlh &&
3091 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003092 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003093 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003094 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003095 table = fib6_new_table(net, cfg->fc_table);
3096 }
3097 } else {
3098 table = fib6_new_table(net, cfg->fc_table);
3099 }
David S. Miller38308472011-12-03 18:02:47 -05003100
3101 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003102 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003103
David Ahern93531c62018-04-17 17:33:25 -07003104 err = -ENOMEM;
3105 rt = fib6_info_alloc(gfp_flags);
3106 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003108
David Ahernd7e774f2018-11-06 12:51:15 -08003109 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3110 extack);
David Ahern767a2212018-10-04 20:07:51 -07003111 if (IS_ERR(rt->fib6_metrics)) {
3112 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003113 /* Do not leave garbage there. */
3114 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003115 goto out;
3116 }
3117
David Ahern93531c62018-04-17 17:33:25 -07003118 if (cfg->fc_flags & RTF_ADDRCONF)
3119 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120
Gao feng1716a962012-04-06 00:13:10 +00003121 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003122 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003123 clock_t_to_jiffies(cfg->fc_expires));
3124 else
David Ahern14895682018-04-17 17:33:17 -07003125 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126
Thomas Graf86872cb2006-08-22 00:01:08 -07003127 if (cfg->fc_protocol == RTPROT_UNSPEC)
3128 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003129 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003130
David Ahern83c442512019-03-27 20:53:50 -07003131 rt->fib6_table = table;
3132 rt->fib6_metric = cfg->fc_metric;
3133 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003134 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003135
David Ahern93c2fb22018-04-18 15:38:59 -07003136 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3137 rt->fib6_dst.plen = cfg->fc_dst_len;
3138 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003139 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003140
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003142 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3143 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144#endif
David Ahern83c442512019-03-27 20:53:50 -07003145 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3146 if (err)
3147 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003148
3149 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003150 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151 */
David Ahern83c442512019-03-27 20:53:50 -07003152 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003153 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003154 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003155
Daniel Walterc3968a82011-04-13 21:10:57 +00003156 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003157 struct net_device *dev = fib6_info_nh_dev(rt);
3158
Daniel Walterc3968a82011-04-13 21:10:57 +00003159 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003160 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003161 err = -EINVAL;
3162 goto out;
3163 }
David Ahern93c2fb22018-04-18 15:38:59 -07003164 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3165 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003166 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003167 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003168
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003169 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170out:
David Ahern93531c62018-04-17 17:33:25 -07003171 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003172 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003173}
3174
David Ahernacb54e32018-04-17 17:33:22 -07003175int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003176 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003177{
David Ahern8d1c8022018-04-17 17:33:26 -07003178 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003179 int err;
3180
David Ahernacb54e32018-04-17 17:33:22 -07003181 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003182 if (IS_ERR(rt))
3183 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003184
David Ahernd4ead6b2018-04-17 17:33:16 -07003185 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003186 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003187
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 return err;
3189}
3190
David Ahern8d1c8022018-04-17 17:33:26 -07003191static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003192{
David Ahernafb1d4b52018-04-17 17:33:11 -07003193 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003194 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003195 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196
David Ahern421842e2018-04-17 17:33:18 -07003197 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003198 err = -ENOENT;
3199 goto out;
3200 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003201
David Ahern93c2fb22018-04-18 15:38:59 -07003202 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003203 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003204 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003205 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206
Gao feng6825a262012-09-19 19:25:34 +00003207out:
David Ahern93531c62018-04-17 17:33:25 -07003208 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 return err;
3210}
3211
David Ahern8d1c8022018-04-17 17:33:26 -07003212int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003213{
David Ahernafb1d4b52018-04-17 17:33:11 -07003214 struct nl_info info = { .nl_net = net };
3215
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003216 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003217}
3218
David Ahern8d1c8022018-04-17 17:33:26 -07003219static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003220{
3221 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003222 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003223 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003224 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003225 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003226
David Ahern421842e2018-04-17 17:33:18 -07003227 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003228 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003229 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003230 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003231
David Ahern93c2fb22018-04-18 15:38:59 -07003232 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003233 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003234
David Ahern16a16cd2017-02-02 12:37:11 -08003235 /* prefer to send a single notification with all hops */
3236 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3237 if (skb) {
3238 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3239
David Ahernd4ead6b2018-04-17 17:33:16 -07003240 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003241 NULL, NULL, 0, RTM_DELROUTE,
3242 info->portid, seq, 0) < 0) {
3243 kfree_skb(skb);
3244 skb = NULL;
3245 } else
3246 info->skip_notify = 1;
3247 }
3248
David Ahern0ae81332017-02-02 12:37:08 -08003249 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003250 &rt->fib6_siblings,
3251 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003252 err = fib6_del(sibling, info);
3253 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003254 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003255 }
3256 }
3257
3258 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003259out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003260 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003261out_put:
David Ahern93531c62018-04-17 17:33:25 -07003262 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003263
3264 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003265 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003266 info->nlh, gfp_any());
3267 }
David Ahern0ae81332017-02-02 12:37:08 -08003268 return err;
3269}
3270
David Ahern23fb93a2018-04-17 17:33:23 -07003271static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3272{
3273 int rc = -ESRCH;
3274
3275 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3276 goto out;
3277
3278 if (cfg->fc_flags & RTF_GATEWAY &&
3279 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3280 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003281
3282 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003283out:
3284 return rc;
3285}
3286
David Ahern333c4302017-05-21 10:12:04 -06003287static int ip6_route_del(struct fib6_config *cfg,
3288 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003289{
David Ahern8d1c8022018-04-17 17:33:26 -07003290 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003291 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003292 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 int err = -ESRCH;
3295
Daniel Lezcano55786892008-03-04 13:47:47 -08003296 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003297 if (!table) {
3298 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003299 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003300 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003301
Wei Wang66f5d6c2017-10-06 12:06:10 -07003302 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003303
3304 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003305 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003306 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003307 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003308
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003310 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003311 struct fib6_nh *nh;
3312
Wei Wang2b760fc2017-10-06 12:06:03 -07003313 if (cfg->fc_flags & RTF_CACHE) {
David Ahern23fb93a2018-04-17 17:33:23 -07003314 int rc;
3315
Wei Wang2b760fc2017-10-06 12:06:03 -07003316 rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst,
3317 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003318 if (rt_cache) {
3319 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003320 if (rc != -ESRCH) {
3321 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003322 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003323 }
David Ahern23fb93a2018-04-17 17:33:23 -07003324 }
3325 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003326 }
David Ahernad1601a2019-03-27 20:53:56 -07003327
3328 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003329 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003330 (!nh->fib_nh_dev ||
3331 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003332 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003333 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003334 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003336 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003338 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003339 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003340 if (!fib6_info_hold_safe(rt))
3341 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003342 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003343
David Ahern0ae81332017-02-02 12:37:08 -08003344 /* if gateway was specified only delete the one hop */
3345 if (cfg->fc_flags & RTF_GATEWAY)
3346 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3347
3348 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003349 }
3350 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003351 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352
3353 return err;
3354}
3355
David S. Miller6700c272012-07-17 03:29:28 -07003356static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003357{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003358 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003359 struct rt6_info *rt, *nrt = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003360 struct ndisc_options ndopts;
3361 struct inet6_dev *in6_dev;
3362 struct neighbour *neigh;
David Aherna68886a2018-04-20 15:38:02 -07003363 struct fib6_info *from;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003364 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003365 int optlen, on_link;
3366 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003367
Simon Horman29a3cad2013-05-28 20:34:26 +00003368 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003369 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003370
3371 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003372 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003373 return;
3374 }
3375
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003376 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003377
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003378 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003379 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003380 return;
3381 }
3382
David S. Miller6e157b62012-07-12 00:05:02 -07003383 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003384 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003385 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003386 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003387 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003388 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003389 return;
3390 }
3391
3392 in6_dev = __in6_dev_get(skb->dev);
3393 if (!in6_dev)
3394 return;
3395 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3396 return;
3397
3398 /* RFC2461 8.1:
3399 * The IP source address of the Redirect MUST be the same as the current
3400 * first-hop router for the specified ICMP Destination Address.
3401 */
3402
Alexander Aringf997c552016-06-15 21:20:23 +02003403 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003404 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3405 return;
3406 }
David S. Miller6e157b62012-07-12 00:05:02 -07003407
3408 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003409 if (ndopts.nd_opts_tgt_lladdr) {
3410 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3411 skb->dev);
3412 if (!lladdr) {
3413 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3414 return;
3415 }
3416 }
3417
David S. Miller6e157b62012-07-12 00:05:02 -07003418 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003419 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003420 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3421 return;
3422 }
3423
3424 /* Redirect received -> path was valid.
3425 * Look, redirects are sent only in response to data packets,
3426 * so that this nexthop apparently is reachable. --ANK
3427 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003428 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003429
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003430 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003431 if (!neigh)
3432 return;
3433
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 /*
3435 * We have finally decided to accept it.
3436 */
3437
Alexander Aringf997c552016-06-15 21:20:23 +02003438 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003439 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3440 NEIGH_UPDATE_F_OVERRIDE|
3441 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003442 NEIGH_UPDATE_F_ISROUTER)),
3443 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444
David Ahern4d85cd02018-04-20 15:37:59 -07003445 rcu_read_lock();
David Aherna68886a2018-04-20 15:38:02 -07003446 from = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003447 /* This fib6_info_hold() is safe here because we hold reference to rt
3448 * and rt already holds reference to fib6_info.
3449 */
David Ahern8a14e462018-04-23 11:32:07 -07003450 fib6_info_hold(from);
David Ahern4d85cd02018-04-20 15:37:59 -07003451 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003452
3453 nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003454 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455 goto out;
3456
3457 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3458 if (on_link)
3459 nrt->rt6i_flags &= ~RTF_GATEWAY;
3460
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003461 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003462
Wei Wang2b760fc2017-10-06 12:06:03 -07003463 /* No need to remove rt from the exception table if rt is
3464 * a cached route because rt6_insert_exception() will
3465 * takes care of it
3466 */
David Ahern8a14e462018-04-23 11:32:07 -07003467 if (rt6_insert_exception(nrt, from)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003468 dst_release_immediate(&nrt->dst);
3469 goto out;
3470 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003471
Changli Gaod8d1f302010-06-10 23:31:35 -07003472 netevent.old = &rt->dst;
3473 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003474 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003475 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003476 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3477
Linus Torvalds1da177e2005-04-16 15:20:36 -07003478out:
David Ahern8a14e462018-04-23 11:32:07 -07003479 fib6_info_release(from);
David S. Millere8599ff2012-07-11 23:43:53 -07003480 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003481}
3482
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003483#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003484static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003485 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003486 const struct in6_addr *gwaddr,
3487 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003488{
David Ahern830218c2016-10-24 10:52:35 -07003489 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3490 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003491 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003492 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003493 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003494
David Ahern830218c2016-10-24 10:52:35 -07003495 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003496 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003497 return NULL;
3498
Wei Wang66f5d6c2017-10-06 12:06:10 -07003499 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003500 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003501 if (!fn)
3502 goto out;
3503
Wei Wang66f5d6c2017-10-06 12:06:10 -07003504 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003505 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003506 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003507 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003508 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003509 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003510 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003511 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003512 if (!fib6_info_hold_safe(rt))
3513 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003514 break;
3515 }
3516out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003517 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003518 return rt;
3519}
3520
David Ahern8d1c8022018-04-17 17:33:26 -07003521static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003522 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003523 const struct in6_addr *gwaddr,
3524 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003525 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003526{
Thomas Graf86872cb2006-08-22 00:01:08 -07003527 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003528 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003529 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003530 .fc_dst_len = prefixlen,
3531 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3532 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003533 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003534 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003535 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003536 .fc_nlinfo.nlh = NULL,
3537 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003538 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003539
David Ahern830218c2016-10-24 10:52:35 -07003540 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003541 cfg.fc_dst = *prefix;
3542 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003543
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003544 /* We should treat it as a default route if prefix length is 0. */
3545 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003546 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003547
David Ahernacb54e32018-04-17 17:33:22 -07003548 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003549
David Ahern830218c2016-10-24 10:52:35 -07003550 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003551}
3552#endif
3553
David Ahern8d1c8022018-04-17 17:33:26 -07003554struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003555 const struct in6_addr *addr,
3556 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003557{
David Ahern830218c2016-10-24 10:52:35 -07003558 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003559 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003560 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003561
David Ahernafb1d4b52018-04-17 17:33:11 -07003562 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003563 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003564 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003565
Wei Wang66f5d6c2017-10-06 12:06:10 -07003566 rcu_read_lock();
3567 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003568 struct fib6_nh *nh = &rt->fib6_nh;
3569
3570 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003571 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003572 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003573 break;
3574 }
Wei Wange873e4b2018-07-21 20:56:32 -07003575 if (rt && !fib6_info_hold_safe(rt))
3576 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003577 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578 return rt;
3579}
3580
David Ahern8d1c8022018-04-17 17:33:26 -07003581struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003582 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003583 struct net_device *dev,
3584 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585{
Thomas Graf86872cb2006-08-22 00:01:08 -07003586 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003587 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003588 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003589 .fc_ifindex = dev->ifindex,
3590 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3591 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003592 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003593 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003594 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003595 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003596 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003597 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003598
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003599 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600
David Ahernacb54e32018-04-17 17:33:22 -07003601 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003602 struct fib6_table *table;
3603
3604 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3605 if (table)
3606 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3607 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003608
David Ahernafb1d4b52018-04-17 17:33:11 -07003609 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610}
3611
David Ahernafb1d4b52018-04-17 17:33:11 -07003612static void __rt6_purge_dflt_routers(struct net *net,
3613 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003614{
David Ahern8d1c8022018-04-17 17:33:26 -07003615 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003616
3617restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003618 rcu_read_lock();
3619 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003620 struct net_device *dev = fib6_info_nh_dev(rt);
3621 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3622
David Ahern93c2fb22018-04-18 15:38:59 -07003623 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003624 (!idev || idev->cnf.accept_ra != 2) &&
3625 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003626 rcu_read_unlock();
3627 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003628 goto restart;
3629 }
3630 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003631 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003632
3633 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3634}
3635
3636void rt6_purge_dflt_routers(struct net *net)
3637{
3638 struct fib6_table *table;
3639 struct hlist_head *head;
3640 unsigned int h;
3641
3642 rcu_read_lock();
3643
3644 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3645 head = &net->ipv6.fib_table_hash[h];
3646 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3647 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003648 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003649 }
3650 }
3651
3652 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653}
3654
Daniel Lezcano55786892008-03-04 13:47:47 -08003655static void rtmsg_to_fib6_config(struct net *net,
3656 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003657 struct fib6_config *cfg)
3658{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003659 *cfg = (struct fib6_config){
3660 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3661 : RT6_TABLE_MAIN,
3662 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003663 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003664 .fc_expires = rtmsg->rtmsg_info,
3665 .fc_dst_len = rtmsg->rtmsg_dst_len,
3666 .fc_src_len = rtmsg->rtmsg_src_len,
3667 .fc_flags = rtmsg->rtmsg_flags,
3668 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003669
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003670 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003671
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003672 .fc_dst = rtmsg->rtmsg_dst,
3673 .fc_src = rtmsg->rtmsg_src,
3674 .fc_gateway = rtmsg->rtmsg_gateway,
3675 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003676}
3677
Daniel Lezcano55786892008-03-04 13:47:47 -08003678int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679{
Thomas Graf86872cb2006-08-22 00:01:08 -07003680 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 struct in6_rtmsg rtmsg;
3682 int err;
3683
Ian Morris67ba4152014-08-24 21:53:10 +01003684 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685 case SIOCADDRT: /* Add a route */
3686 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003687 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688 return -EPERM;
3689 err = copy_from_user(&rtmsg, arg,
3690 sizeof(struct in6_rtmsg));
3691 if (err)
3692 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003693
Daniel Lezcano55786892008-03-04 13:47:47 -08003694 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003695
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696 rtnl_lock();
3697 switch (cmd) {
3698 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003699 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003700 break;
3701 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003702 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 break;
3704 default:
3705 err = -EINVAL;
3706 }
3707 rtnl_unlock();
3708
3709 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711
3712 return -EINVAL;
3713}
3714
3715/*
3716 * Drop the packet on the floor
3717 */
3718
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003719static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003721 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003722 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003723 switch (ipstats_mib_noroutes) {
3724 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003725 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003726 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003727 IP6_INC_STATS(dev_net(dst->dev),
3728 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003729 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003730 break;
3731 }
3732 /* FALLTHROUGH */
3733 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003734 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3735 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003736 break;
3737 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003738 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739 kfree_skb(skb);
3740 return 0;
3741}
3742
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003743static int ip6_pkt_discard(struct sk_buff *skb)
3744{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003745 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003746}
3747
Eric W. Biedermanede20592015-10-07 16:48:47 -05003748static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003749{
Eric Dumazetadf30902009-06-02 05:19:30 +00003750 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003751 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752}
3753
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003754static int ip6_pkt_prohibit(struct sk_buff *skb)
3755{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003756 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003757}
3758
Eric W. Biedermanede20592015-10-07 16:48:47 -05003759static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003760{
Eric Dumazetadf30902009-06-02 05:19:30 +00003761 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003762 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003763}
3764
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765/*
3766 * Allocate a dst for local (unicast / anycast) address.
3767 */
3768
David Ahern360a9882018-04-18 15:39:00 -07003769struct fib6_info *addrconf_f6i_alloc(struct net *net,
3770 struct inet6_dev *idev,
3771 const struct in6_addr *addr,
3772 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773{
David Ahernc7a1ce32019-03-21 05:21:35 -07003774 struct fib6_config cfg = {
3775 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3776 .fc_ifindex = idev->dev->ifindex,
3777 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3778 .fc_dst = *addr,
3779 .fc_dst_len = 128,
3780 .fc_protocol = RTPROT_KERNEL,
3781 .fc_nlinfo.nl_net = net,
3782 .fc_ignore_dev_down = true,
3783 };
David Ahern5f02ce242016-09-10 12:09:54 -07003784
David Aherne8478e82018-04-17 17:33:13 -07003785 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003786 cfg.fc_type = RTN_ANYCAST;
3787 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003788 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003789 cfg.fc_type = RTN_LOCAL;
3790 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792
David Ahernc7a1ce32019-03-21 05:21:35 -07003793 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794}
3795
Daniel Walterc3968a82011-04-13 21:10:57 +00003796/* remove deleted ip from prefsrc entries */
3797struct arg_dev_net_ip {
3798 struct net_device *dev;
3799 struct net *net;
3800 struct in6_addr *addr;
3801};
3802
David Ahern8d1c8022018-04-17 17:33:26 -07003803static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003804{
3805 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3806 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3807 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3808
David Ahernad1601a2019-03-27 20:53:56 -07003809 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003810 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003811 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003812 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003813 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003814 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003815 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003816 }
3817 return 0;
3818}
3819
3820void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3821{
3822 struct net *net = dev_net(ifp->idev->dev);
3823 struct arg_dev_net_ip adni = {
3824 .dev = ifp->idev->dev,
3825 .net = net,
3826 .addr = &ifp->addr,
3827 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003828 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003829}
3830
David Ahern2b2450c2019-03-27 20:53:52 -07003831#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003832
3833/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003834static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003835{
3836 struct in6_addr *gateway = (struct in6_addr *)arg;
3837
David Ahern93c2fb22018-04-18 15:38:59 -07003838 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003839 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003840 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003841 return -1;
3842 }
Wei Wangb16cb452017-10-06 12:06:00 -07003843
3844 /* Further clean up cached routes in exception table.
3845 * This is needed because cached route may have a different
3846 * gateway than its 'parent' in the case of an ip redirect.
3847 */
3848 rt6_exceptions_clean_tohost(rt, gateway);
3849
Duan Jiongbe7a0102014-05-15 15:56:14 +08003850 return 0;
3851}
3852
3853void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3854{
3855 fib6_clean_all(net, fib6_clean_tohost, gateway);
3856}
3857
Ido Schimmel2127d952018-01-07 12:45:03 +02003858struct arg_netdev_event {
3859 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003860 union {
3861 unsigned int nh_flags;
3862 unsigned long event;
3863 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003864};
3865
David Ahern8d1c8022018-04-17 17:33:26 -07003866static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003867{
David Ahern8d1c8022018-04-17 17:33:26 -07003868 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003869 struct fib6_node *fn;
3870
David Ahern93c2fb22018-04-18 15:38:59 -07003871 fn = rcu_dereference_protected(rt->fib6_node,
3872 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003873 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003874 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003875 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003876 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003877 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003878 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003879 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003880 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003881 }
3882
3883 return NULL;
3884}
3885
David Ahern8d1c8022018-04-17 17:33:26 -07003886static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003887{
David Ahernad1601a2019-03-27 20:53:56 -07003888 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3889 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3890 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003891 return true;
3892
3893 return false;
3894}
3895
David Ahern8d1c8022018-04-17 17:33:26 -07003896static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003897{
David Ahern8d1c8022018-04-17 17:33:26 -07003898 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003899 int total = 0;
3900
3901 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003902 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003903
David Ahern93c2fb22018-04-18 15:38:59 -07003904 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003905 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003906 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003907 }
3908
3909 return total;
3910}
3911
David Ahern8d1c8022018-04-17 17:33:26 -07003912static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003913{
3914 int upper_bound = -1;
3915
3916 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003917 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003918 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3919 total) - 1;
3920 }
David Ahernad1601a2019-03-27 20:53:56 -07003921 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003922}
3923
David Ahern8d1c8022018-04-17 17:33:26 -07003924static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003925{
David Ahern8d1c8022018-04-17 17:33:26 -07003926 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003927 int weight = 0;
3928
3929 rt6_upper_bound_set(rt, &weight, total);
3930
David Ahern93c2fb22018-04-18 15:38:59 -07003931 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003932 rt6_upper_bound_set(iter, &weight, total);
3933}
3934
David Ahern8d1c8022018-04-17 17:33:26 -07003935void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003936{
David Ahern8d1c8022018-04-17 17:33:26 -07003937 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003938 int total;
3939
3940 /* In case the entire multipath route was marked for flushing,
3941 * then there is no need to rebalance upon the removal of every
3942 * sibling route.
3943 */
David Ahern93c2fb22018-04-18 15:38:59 -07003944 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003945 return;
3946
3947 /* During lookup routes are evaluated in order, so we need to
3948 * make sure upper bounds are assigned from the first sibling
3949 * onwards.
3950 */
3951 first = rt6_multipath_first_sibling(rt);
3952 if (WARN_ON_ONCE(!first))
3953 return;
3954
3955 total = rt6_multipath_total_weight(first);
3956 rt6_multipath_upper_bound_set(first, total);
3957}
3958
David Ahern8d1c8022018-04-17 17:33:26 -07003959static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02003960{
3961 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07003962 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02003963
David Ahernad1601a2019-03-27 20:53:56 -07003964 if (rt != net->ipv6.fib6_null_entry &&
3965 rt->fib6_nh.fib_nh_dev == arg->dev) {
3966 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07003967 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003968 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02003969 }
Ido Schimmel2127d952018-01-07 12:45:03 +02003970
3971 return 0;
3972}
3973
3974void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
3975{
3976 struct arg_netdev_event arg = {
3977 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02003978 {
3979 .nh_flags = nh_flags,
3980 },
Ido Schimmel2127d952018-01-07 12:45:03 +02003981 };
3982
3983 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
3984 arg.nh_flags |= RTNH_F_LINKDOWN;
3985
3986 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
3987}
3988
David Ahern8d1c8022018-04-17 17:33:26 -07003989static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02003990 const struct net_device *dev)
3991{
David Ahern8d1c8022018-04-17 17:33:26 -07003992 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02003993
David Ahernad1601a2019-03-27 20:53:56 -07003994 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02003995 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07003996 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07003997 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02003998 return true;
3999
4000 return false;
4001}
4002
David Ahern8d1c8022018-04-17 17:33:26 -07004003static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004004{
David Ahern8d1c8022018-04-17 17:33:26 -07004005 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004006
4007 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004008 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004009 iter->should_flush = 1;
4010}
4011
David Ahern8d1c8022018-04-17 17:33:26 -07004012static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004013 const struct net_device *down_dev)
4014{
David Ahern8d1c8022018-04-17 17:33:26 -07004015 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004016 unsigned int dead = 0;
4017
David Ahernad1601a2019-03-27 20:53:56 -07004018 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4019 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004020 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004021 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004022 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4023 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004024 dead++;
4025
4026 return dead;
4027}
4028
David Ahern8d1c8022018-04-17 17:33:26 -07004029static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004030 const struct net_device *dev,
4031 unsigned int nh_flags)
4032{
David Ahern8d1c8022018-04-17 17:33:26 -07004033 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004034
David Ahernad1601a2019-03-27 20:53:56 -07004035 if (rt->fib6_nh.fib_nh_dev == dev)
4036 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004037 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004038 if (iter->fib6_nh.fib_nh_dev == dev)
4039 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004040}
4041
David Aherna1a22c12017-01-18 07:40:36 -08004042/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004043static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004044{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004045 const struct arg_netdev_event *arg = p_arg;
4046 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004047 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004048
David Ahern421842e2018-04-17 17:33:18 -07004049 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004050 return 0;
4051
4052 switch (arg->event) {
4053 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004054 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004055 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004056 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004057 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004058 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004059 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004060 if (rt6_multipath_uses_dev(rt, dev)) {
4061 unsigned int count;
4062
4063 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004064 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004065 rt6_multipath_flush(rt);
4066 return -1;
4067 }
4068 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4069 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004070 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004071 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004072 }
4073 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004074 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004075 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004076 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004077 break;
David Ahernad1601a2019-03-27 20:53:56 -07004078 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004079 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004080 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004081 }
David S. Millerc159d302011-12-26 15:24:36 -05004082
Linus Torvalds1da177e2005-04-16 15:20:36 -07004083 return 0;
4084}
4085
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004086void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004087{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004088 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004089 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004090 {
4091 .event = event,
4092 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004093 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004094 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004095
David Ahern7c6bb7d2018-10-11 20:17:21 -07004096 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4097 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4098 else
4099 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004100}
4101
4102void rt6_disable_ip(struct net_device *dev, unsigned long event)
4103{
4104 rt6_sync_down_dev(dev, event);
4105 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4106 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004107}
4108
Eric Dumazet95c96172012-04-15 05:58:06 +00004109struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004110 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004111 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004112};
4113
David Ahern8d1c8022018-04-17 17:33:26 -07004114static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004115{
4116 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4117 struct inet6_dev *idev;
4118
4119 /* In IPv6 pmtu discovery is not optional,
4120 so that RTAX_MTU lock cannot disable it.
4121 We still use this lock to block changes
4122 caused by addrconf/ndisc.
4123 */
4124
4125 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004126 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127 return 0;
4128
4129 /* For administrative MTU increase, there is no way to discover
4130 IPv6 PMTU increase, so PMTU increase should be updated here.
4131 Since RFC 1981 doesn't include administrative MTU increase
4132 update PMTU increase is a MUST. (i.e. jumbo frame)
4133 */
David Ahernad1601a2019-03-27 20:53:56 -07004134 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004135 !fib6_metric_locked(rt, RTAX_MTU)) {
4136 u32 mtu = rt->fib6_pmtu;
4137
4138 if (mtu >= arg->mtu ||
4139 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4140 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4141
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004142 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004143 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004144 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004145 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004146 return 0;
4147}
4148
Eric Dumazet95c96172012-04-15 05:58:06 +00004149void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004150{
Thomas Grafc71099a2006-08-04 23:20:06 -07004151 struct rt6_mtu_change_arg arg = {
4152 .dev = dev,
4153 .mtu = mtu,
4154 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004155
Li RongQing0c3584d2013-12-27 16:32:38 +08004156 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004157}
4158
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004159static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004160 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004161 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004162 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004163 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004164 [RTA_PRIORITY] = { .type = NLA_U32 },
4165 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004166 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004167 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004168 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4169 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004170 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004171 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004172 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004173 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004174 [RTA_IP_PROTO] = { .type = NLA_U8 },
4175 [RTA_SPORT] = { .type = NLA_U16 },
4176 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004177};
4178
4179static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004180 struct fib6_config *cfg,
4181 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004182{
Thomas Graf86872cb2006-08-22 00:01:08 -07004183 struct rtmsg *rtm;
4184 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004185 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004186 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187
Johannes Bergfceb6432017-04-12 14:34:07 +02004188 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004189 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004190 if (err < 0)
4191 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192
Thomas Graf86872cb2006-08-22 00:01:08 -07004193 err = -EINVAL;
4194 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004195
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004196 *cfg = (struct fib6_config){
4197 .fc_table = rtm->rtm_table,
4198 .fc_dst_len = rtm->rtm_dst_len,
4199 .fc_src_len = rtm->rtm_src_len,
4200 .fc_flags = RTF_UP,
4201 .fc_protocol = rtm->rtm_protocol,
4202 .fc_type = rtm->rtm_type,
4203
4204 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4205 .fc_nlinfo.nlh = nlh,
4206 .fc_nlinfo.nl_net = sock_net(skb->sk),
4207 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004208
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004209 if (rtm->rtm_type == RTN_UNREACHABLE ||
4210 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004211 rtm->rtm_type == RTN_PROHIBIT ||
4212 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004213 cfg->fc_flags |= RTF_REJECT;
4214
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004215 if (rtm->rtm_type == RTN_LOCAL)
4216 cfg->fc_flags |= RTF_LOCAL;
4217
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004218 if (rtm->rtm_flags & RTM_F_CLONED)
4219 cfg->fc_flags |= RTF_CACHE;
4220
David Ahernfc1e64e2018-01-25 16:55:09 -08004221 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4222
Thomas Graf86872cb2006-08-22 00:01:08 -07004223 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004224 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004225 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004226 }
David Aherne3818542019-02-26 09:00:03 -08004227 if (tb[RTA_VIA]) {
4228 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4229 goto errout;
4230 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004231
4232 if (tb[RTA_DST]) {
4233 int plen = (rtm->rtm_dst_len + 7) >> 3;
4234
4235 if (nla_len(tb[RTA_DST]) < plen)
4236 goto errout;
4237
4238 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004239 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004240
4241 if (tb[RTA_SRC]) {
4242 int plen = (rtm->rtm_src_len + 7) >> 3;
4243
4244 if (nla_len(tb[RTA_SRC]) < plen)
4245 goto errout;
4246
4247 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004248 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004249
Daniel Walterc3968a82011-04-13 21:10:57 +00004250 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004251 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004252
Thomas Graf86872cb2006-08-22 00:01:08 -07004253 if (tb[RTA_OIF])
4254 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4255
4256 if (tb[RTA_PRIORITY])
4257 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4258
4259 if (tb[RTA_METRICS]) {
4260 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4261 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004262 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004263
4264 if (tb[RTA_TABLE])
4265 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4266
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004267 if (tb[RTA_MULTIPATH]) {
4268 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4269 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004270
4271 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004272 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004273 if (err < 0)
4274 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004275 }
4276
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004277 if (tb[RTA_PREF]) {
4278 pref = nla_get_u8(tb[RTA_PREF]);
4279 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4280 pref != ICMPV6_ROUTER_PREF_HIGH)
4281 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4282 cfg->fc_flags |= RTF_PREF(pref);
4283 }
4284
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004285 if (tb[RTA_ENCAP])
4286 cfg->fc_encap = tb[RTA_ENCAP];
4287
David Ahern9ed59592017-01-17 14:57:36 -08004288 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004289 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4290
David Ahernc255bd62017-05-27 16:19:27 -06004291 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004292 if (err < 0)
4293 goto errout;
4294 }
4295
Xin Long32bc2012015-12-16 17:50:11 +08004296 if (tb[RTA_EXPIRES]) {
4297 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4298
4299 if (addrconf_finite_timeout(timeout)) {
4300 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4301 cfg->fc_flags |= RTF_EXPIRES;
4302 }
4303 }
4304
Thomas Graf86872cb2006-08-22 00:01:08 -07004305 err = 0;
4306errout:
4307 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004308}
4309
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004310struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004311 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004312 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004313 struct list_head next;
4314};
4315
David Ahernd4ead6b2018-04-17 17:33:16 -07004316static int ip6_route_info_append(struct net *net,
4317 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004318 struct fib6_info *rt,
4319 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004320{
4321 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004322 int err = -EEXIST;
4323
4324 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004325 /* check if fib6_info already exists */
4326 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004327 return err;
4328 }
4329
4330 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4331 if (!nh)
4332 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004333 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004334 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4335 list_add_tail(&nh->next, rt6_nh_list);
4336
4337 return 0;
4338}
4339
David Ahern8d1c8022018-04-17 17:33:26 -07004340static void ip6_route_mpath_notify(struct fib6_info *rt,
4341 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004342 struct nl_info *info,
4343 __u16 nlflags)
4344{
4345 /* if this is an APPEND route, then rt points to the first route
4346 * inserted and rt_last points to last route inserted. Userspace
4347 * wants a consistent dump of the route which starts at the first
4348 * nexthop. Since sibling routes are always added at the end of
4349 * the list, find the first sibling of the last route appended
4350 */
David Ahern93c2fb22018-04-18 15:38:59 -07004351 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4352 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004353 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004354 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004355 }
4356
4357 if (rt)
4358 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4359}
4360
David Ahern333c4302017-05-21 10:12:04 -06004361static int ip6_route_multipath_add(struct fib6_config *cfg,
4362 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004363{
David Ahern8d1c8022018-04-17 17:33:26 -07004364 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004365 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004366 struct fib6_config r_cfg;
4367 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004368 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004369 struct rt6_nh *err_nh;
4370 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004371 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004372 int remaining;
4373 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004374 int err = 1;
4375 int nhn = 0;
4376 int replace = (cfg->fc_nlinfo.nlh &&
4377 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4378 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004379
David Ahern3b1137f2017-02-02 12:37:10 -08004380 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4381 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4382 nlflags |= NLM_F_APPEND;
4383
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004384 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004385 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004386
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004387 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004388 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004389 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004390 while (rtnh_ok(rtnh, remaining)) {
4391 memcpy(&r_cfg, cfg, sizeof(*cfg));
4392 if (rtnh->rtnh_ifindex)
4393 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4394
4395 attrlen = rtnh_attrlen(rtnh);
4396 if (attrlen > 0) {
4397 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4398
4399 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4400 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004401 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004402 r_cfg.fc_flags |= RTF_GATEWAY;
4403 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004404 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4405 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4406 if (nla)
4407 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004408 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004409
David Ahern68e2ffd2018-03-20 10:06:59 -07004410 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004411 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004412 if (IS_ERR(rt)) {
4413 err = PTR_ERR(rt);
4414 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004415 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004416 }
David Ahernb5d2d752018-07-15 09:35:19 -07004417 if (!rt6_qualify_for_ecmp(rt)) {
4418 err = -EINVAL;
4419 NL_SET_ERR_MSG(extack,
4420 "Device only routes can not be added for IPv6 using the multipath API.");
4421 fib6_info_release(rt);
4422 goto cleanup;
4423 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004424
David Ahernad1601a2019-03-27 20:53:56 -07004425 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004426
David Ahernd4ead6b2018-04-17 17:33:16 -07004427 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4428 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004429 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004430 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004431 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004432 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004433
4434 rtnh = rtnh_next(rtnh, &remaining);
4435 }
4436
David Ahern3b1137f2017-02-02 12:37:10 -08004437 /* for add and replace send one notification with all nexthops.
4438 * Skip the notification in fib6_add_rt2node and send one with
4439 * the full route when done
4440 */
4441 info->skip_notify = 1;
4442
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004443 err_nh = NULL;
4444 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004445 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4446 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004447
David Ahernf7225172018-06-04 13:41:42 -07004448 if (!err) {
4449 /* save reference to last route successfully inserted */
4450 rt_last = nh->fib6_info;
4451
4452 /* save reference to first route for notification */
4453 if (!rt_notif)
4454 rt_notif = nh->fib6_info;
4455 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004456
David Ahern8d1c8022018-04-17 17:33:26 -07004457 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4458 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004459 if (err) {
4460 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004461 NL_SET_ERR_MSG_MOD(extack,
4462 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004463 err_nh = nh;
4464 goto add_errout;
4465 }
4466
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004467 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004468 * these flags after the first nexthop: if there is a collision,
4469 * we have already failed to add the first nexthop:
4470 * fib6_add_rt2node() has rejected it; when replacing, old
4471 * nexthops have been replaced by first new, the rest should
4472 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004473 */
Michal Kubeček27596472015-05-18 20:54:00 +02004474 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4475 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004476 nhn++;
4477 }
4478
David Ahern3b1137f2017-02-02 12:37:10 -08004479 /* success ... tell user about new route */
4480 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004481 goto cleanup;
4482
4483add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004484 /* send notification for routes that were added so that
4485 * the delete notifications sent by ip6_route_del are
4486 * coherent
4487 */
4488 if (rt_notif)
4489 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4490
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004491 /* Delete routes that were already added */
4492 list_for_each_entry(nh, &rt6_nh_list, next) {
4493 if (err_nh == nh)
4494 break;
David Ahern333c4302017-05-21 10:12:04 -06004495 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004496 }
4497
4498cleanup:
4499 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004500 if (nh->fib6_info)
4501 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004502 list_del(&nh->next);
4503 kfree(nh);
4504 }
4505
4506 return err;
4507}
4508
David Ahern333c4302017-05-21 10:12:04 -06004509static int ip6_route_multipath_del(struct fib6_config *cfg,
4510 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004511{
4512 struct fib6_config r_cfg;
4513 struct rtnexthop *rtnh;
4514 int remaining;
4515 int attrlen;
4516 int err = 1, last_err = 0;
4517
4518 remaining = cfg->fc_mp_len;
4519 rtnh = (struct rtnexthop *)cfg->fc_mp;
4520
4521 /* Parse a Multipath Entry */
4522 while (rtnh_ok(rtnh, remaining)) {
4523 memcpy(&r_cfg, cfg, sizeof(*cfg));
4524 if (rtnh->rtnh_ifindex)
4525 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4526
4527 attrlen = rtnh_attrlen(rtnh);
4528 if (attrlen > 0) {
4529 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4530
4531 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4532 if (nla) {
4533 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4534 r_cfg.fc_flags |= RTF_GATEWAY;
4535 }
4536 }
David Ahern333c4302017-05-21 10:12:04 -06004537 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004538 if (err)
4539 last_err = err;
4540
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004541 rtnh = rtnh_next(rtnh, &remaining);
4542 }
4543
4544 return last_err;
4545}
4546
David Ahernc21ef3e2017-04-16 09:48:24 -07004547static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4548 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004549{
Thomas Graf86872cb2006-08-22 00:01:08 -07004550 struct fib6_config cfg;
4551 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004552
David Ahern333c4302017-05-21 10:12:04 -06004553 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004554 if (err < 0)
4555 return err;
4556
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004557 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004558 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004559 else {
4560 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004561 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004562 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004563}
4564
David Ahernc21ef3e2017-04-16 09:48:24 -07004565static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4566 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004567{
Thomas Graf86872cb2006-08-22 00:01:08 -07004568 struct fib6_config cfg;
4569 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004570
David Ahern333c4302017-05-21 10:12:04 -06004571 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004572 if (err < 0)
4573 return err;
4574
David Ahern67f69512019-03-21 05:21:34 -07004575 if (cfg.fc_metric == 0)
4576 cfg.fc_metric = IP6_RT_PRIO_USER;
4577
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004578 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004579 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004580 else
David Ahernacb54e32018-04-17 17:33:22 -07004581 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004582}
4583
David Ahern8d1c8022018-04-17 17:33:26 -07004584static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004585{
David Ahernbeb1afac52017-02-02 12:37:09 -08004586 int nexthop_len = 0;
4587
David Ahern93c2fb22018-04-18 15:38:59 -07004588 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004589 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4590 + NLA_ALIGN(sizeof(struct rtnexthop))
4591 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004592 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004593
David Ahern93c2fb22018-04-18 15:38:59 -07004594 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004595 }
4596
Thomas Graf339bf982006-11-10 14:10:15 -08004597 return NLMSG_ALIGN(sizeof(struct rtmsg))
4598 + nla_total_size(16) /* RTA_SRC */
4599 + nla_total_size(16) /* RTA_DST */
4600 + nla_total_size(16) /* RTA_GATEWAY */
4601 + nla_total_size(16) /* RTA_PREFSRC */
4602 + nla_total_size(4) /* RTA_TABLE */
4603 + nla_total_size(4) /* RTA_IIF */
4604 + nla_total_size(4) /* RTA_OIF */
4605 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004606 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004607 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004608 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004609 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004610 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004611 + nexthop_len;
4612}
4613
David Ahernd4ead6b2018-04-17 17:33:16 -07004614static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004615 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004616 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004617 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004618 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004619{
Xin Long22d0bd82018-09-11 14:33:58 +08004620 struct rt6_info *rt6 = (struct rt6_info *)dst;
4621 struct rt6key *rt6_dst, *rt6_src;
4622 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004623 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004624 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004625 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004626
Eric W. Biederman15e47302012-09-07 20:12:54 +00004627 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004628 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004629 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004630
Xin Long22d0bd82018-09-11 14:33:58 +08004631 if (rt6) {
4632 rt6_dst = &rt6->rt6i_dst;
4633 rt6_src = &rt6->rt6i_src;
4634 rt6_flags = rt6->rt6i_flags;
4635 } else {
4636 rt6_dst = &rt->fib6_dst;
4637 rt6_src = &rt->fib6_src;
4638 rt6_flags = rt->fib6_flags;
4639 }
4640
Thomas Graf2d7202b2006-08-22 00:01:27 -07004641 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004642 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004643 rtm->rtm_dst_len = rt6_dst->plen;
4644 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004646 if (rt->fib6_table)
4647 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004648 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004649 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004650 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004651 if (nla_put_u32(skb, RTA_TABLE, table))
4652 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004653
4654 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004655 rtm->rtm_flags = 0;
4656 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004657 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004658
Xin Long22d0bd82018-09-11 14:33:58 +08004659 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660 rtm->rtm_flags |= RTM_F_CLONED;
4661
David Ahernd4ead6b2018-04-17 17:33:16 -07004662 if (dest) {
4663 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004664 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004665 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004667 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004668 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004669#ifdef CONFIG_IPV6_SUBTREES
4670 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004671 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004672 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004673 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004674 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004675 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004676 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004678 if (iif) {
4679#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004680 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004681 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004682
David Ahernfd61c6b2017-01-17 15:51:07 -08004683 if (err == 0)
4684 return 0;
4685 if (err < 0)
4686 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004687 } else
4688#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004689 if (nla_put_u32(skb, RTA_IIF, iif))
4690 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004691 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004692 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004693 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004694 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004695 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004697
David Ahern93c2fb22018-04-18 15:38:59 -07004698 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004699 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004700 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004701 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004702 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004703 }
4704
David Ahernd4ead6b2018-04-17 17:33:16 -07004705 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4706 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004707 goto nla_put_failure;
4708
David Ahern93c2fb22018-04-18 15:38:59 -07004709 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004710 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004711
David Ahernbeb1afac52017-02-02 12:37:09 -08004712 /* For multipath routes, walk the siblings list and add
4713 * each as a nexthop within RTA_MULTIPATH.
4714 */
Xin Long22d0bd82018-09-11 14:33:58 +08004715 if (rt6) {
4716 if (rt6_flags & RTF_GATEWAY &&
4717 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4718 goto nla_put_failure;
4719
4720 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4721 goto nla_put_failure;
4722 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004723 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004724 struct nlattr *mp;
4725
4726 mp = nla_nest_start(skb, RTA_MULTIPATH);
4727 if (!mp)
4728 goto nla_put_failure;
4729
David Ahernc0a72072019-04-02 14:11:58 -07004730 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4731 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004732 goto nla_put_failure;
4733
4734 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004735 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004736 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4737 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004738 goto nla_put_failure;
4739 }
4740
4741 nla_nest_end(skb, mp);
4742 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004743 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4744 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004745 goto nla_put_failure;
4746 }
4747
Xin Long22d0bd82018-09-11 14:33:58 +08004748 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004749 expires = dst ? dst->expires : rt->expires;
4750 expires -= jiffies;
4751 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004752
David Ahernd4ead6b2018-04-17 17:33:16 -07004753 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004754 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004755
Xin Long22d0bd82018-09-11 14:33:58 +08004756 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004757 goto nla_put_failure;
4758
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004759
Johannes Berg053c0952015-01-16 22:09:00 +01004760 nlmsg_end(skb, nlh);
4761 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004762
4763nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004764 nlmsg_cancel(skb, nlh);
4765 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766}
4767
David Ahern13e38902018-10-15 18:56:44 -07004768static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4769 const struct net_device *dev)
4770{
David Ahernad1601a2019-03-27 20:53:56 -07004771 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004772 return true;
4773
4774 if (f6i->fib6_nsiblings) {
4775 struct fib6_info *sibling, *next_sibling;
4776
4777 list_for_each_entry_safe(sibling, next_sibling,
4778 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004779 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004780 return true;
4781 }
4782 }
4783
4784 return false;
4785}
4786
David Ahern8d1c8022018-04-17 17:33:26 -07004787int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004788{
4789 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004790 struct fib_dump_filter *filter = &arg->filter;
4791 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004792 struct net *net = arg->net;
4793
David Ahern421842e2018-04-17 17:33:18 -07004794 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004795 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004796
David Ahern13e38902018-10-15 18:56:44 -07004797 if ((filter->flags & RTM_F_PREFIX) &&
4798 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4799 /* success since this is not a prefix route */
4800 return 1;
4801 }
4802 if (filter->filter_set) {
4803 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4804 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4805 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004806 return 1;
4807 }
David Ahern13e38902018-10-15 18:56:44 -07004808 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004810
David Ahernd4ead6b2018-04-17 17:33:16 -07004811 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4812 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004813 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004814}
4815
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004816static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4817 const struct nlmsghdr *nlh,
4818 struct nlattr **tb,
4819 struct netlink_ext_ack *extack)
4820{
4821 struct rtmsg *rtm;
4822 int i, err;
4823
4824 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4825 NL_SET_ERR_MSG_MOD(extack,
4826 "Invalid header for get route request");
4827 return -EINVAL;
4828 }
4829
4830 if (!netlink_strict_get_check(skb))
4831 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4832 rtm_ipv6_policy, extack);
4833
4834 rtm = nlmsg_data(nlh);
4835 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4836 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4837 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4838 rtm->rtm_type) {
4839 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4840 return -EINVAL;
4841 }
4842 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4843 NL_SET_ERR_MSG_MOD(extack,
4844 "Invalid flags for get route request");
4845 return -EINVAL;
4846 }
4847
4848 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4849 rtm_ipv6_policy, extack);
4850 if (err)
4851 return err;
4852
4853 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4854 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4855 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4856 return -EINVAL;
4857 }
4858
4859 for (i = 0; i <= RTA_MAX; i++) {
4860 if (!tb[i])
4861 continue;
4862
4863 switch (i) {
4864 case RTA_SRC:
4865 case RTA_DST:
4866 case RTA_IIF:
4867 case RTA_OIF:
4868 case RTA_MARK:
4869 case RTA_UID:
4870 case RTA_SPORT:
4871 case RTA_DPORT:
4872 case RTA_IP_PROTO:
4873 break;
4874 default:
4875 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4876 return -EINVAL;
4877 }
4878 }
4879
4880 return 0;
4881}
4882
David Ahernc21ef3e2017-04-16 09:48:24 -07004883static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4884 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004885{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004886 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004887 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004888 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004889 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004890 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004891 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004892 struct sk_buff *skb;
4893 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004894 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004895 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004896
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004897 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004898 if (err < 0)
4899 goto errout;
4900
4901 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004902 rtm = nlmsg_data(nlh);
4903 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004904 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004905
4906 if (tb[RTA_SRC]) {
4907 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4908 goto errout;
4909
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004910 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004911 }
4912
4913 if (tb[RTA_DST]) {
4914 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4915 goto errout;
4916
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004917 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004918 }
4919
4920 if (tb[RTA_IIF])
4921 iif = nla_get_u32(tb[RTA_IIF]);
4922
4923 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004924 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004925
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004926 if (tb[RTA_MARK])
4927 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4928
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004929 if (tb[RTA_UID])
4930 fl6.flowi6_uid = make_kuid(current_user_ns(),
4931 nla_get_u32(tb[RTA_UID]));
4932 else
4933 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4934
Roopa Prabhueacb9382018-05-22 14:03:28 -07004935 if (tb[RTA_SPORT])
4936 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4937
4938 if (tb[RTA_DPORT])
4939 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4940
4941 if (tb[RTA_IP_PROTO]) {
4942 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004943 &fl6.flowi6_proto, AF_INET6,
4944 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004945 if (err)
4946 goto errout;
4947 }
4948
Thomas Grafab364a62006-08-22 00:01:47 -07004949 if (iif) {
4950 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004951 int flags = 0;
4952
Florian Westphal121622d2017-08-15 16:34:42 +02004953 rcu_read_lock();
4954
4955 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07004956 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02004957 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07004958 err = -ENODEV;
4959 goto errout;
4960 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004961
4962 fl6.flowi6_iif = iif;
4963
4964 if (!ipv6_addr_any(&fl6.saddr))
4965 flags |= RT6_LOOKUP_F_HAS_SADDR;
4966
David Ahernb75cc8f2018-03-02 08:32:17 -08004967 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02004968
4969 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004970 } else {
4971 fl6.flowi6_oif = oif;
4972
Ido Schimmel58acfd72017-12-20 12:28:25 +02004973 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004974 }
4975
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004976
4977 rt = container_of(dst, struct rt6_info, dst);
4978 if (rt->dst.error) {
4979 err = rt->dst.error;
4980 ip6_rt_put(rt);
4981 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07004982 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983
WANG Cong9d6acb32017-03-01 20:48:39 -08004984 if (rt == net->ipv6.ip6_null_entry) {
4985 err = rt->dst.error;
4986 ip6_rt_put(rt);
4987 goto errout;
4988 }
4989
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05004991 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00004992 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07004993 err = -ENOBUFS;
4994 goto errout;
4995 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996
Changli Gaod8d1f302010-06-10 23:31:35 -07004997 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07004998
4999 rcu_read_lock();
5000 from = rcu_dereference(rt->from);
5001
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005002 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005003 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005004 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5005 nlh->nlmsg_seq, 0);
5006 else
David Aherna68886a2018-04-20 15:38:02 -07005007 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5008 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005009 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5010 0);
David Aherna68886a2018-04-20 15:38:02 -07005011 rcu_read_unlock();
5012
Linus Torvalds1da177e2005-04-16 15:20:36 -07005013 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005014 kfree_skb(skb);
5015 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005016 }
5017
Eric W. Biederman15e47302012-09-07 20:12:54 +00005018 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005019errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005020 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005021}
5022
David Ahern8d1c8022018-04-17 17:33:26 -07005023void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005024 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025{
5026 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005027 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005028 u32 seq;
5029 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005030
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005031 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005032 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005033
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005034 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005035 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005036 goto errout;
5037
David Ahernd4ead6b2018-04-17 17:33:16 -07005038 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5039 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005040 if (err < 0) {
5041 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5042 WARN_ON(err == -EMSGSIZE);
5043 kfree_skb(skb);
5044 goto errout;
5045 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005046 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005047 info->nlh, gfp_any());
5048 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005049errout:
5050 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005051 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052}
5053
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005054static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005055 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005056{
Jiri Pirko351638e2013-05-28 01:30:21 +00005057 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005058 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005059
WANG Cong242d3a42017-05-08 10:12:13 -07005060 if (!(dev->flags & IFF_LOOPBACK))
5061 return NOTIFY_OK;
5062
5063 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005064 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005065 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005066 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5067#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005068 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005069 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005070 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005071 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5072#endif
WANG Cong76da0702017-06-20 11:42:27 -07005073 } else if (event == NETDEV_UNREGISTER &&
5074 dev->reg_state != NETREG_UNREGISTERED) {
5075 /* NETDEV_UNREGISTER could be fired for multiple times by
5076 * netdev_wait_allrefs(). Make sure we only call this once.
5077 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005078 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005079#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005080 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5081 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005082#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005083 }
5084
5085 return NOTIFY_OK;
5086}
5087
Linus Torvalds1da177e2005-04-16 15:20:36 -07005088/*
5089 * /proc
5090 */
5091
5092#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005093static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5094{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005095 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005096 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005097 net->ipv6.rt6_stats->fib_nodes,
5098 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005099 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005100 net->ipv6.rt6_stats->fib_rt_entries,
5101 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005102 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005103 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005104
5105 return 0;
5106}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005107#endif /* CONFIG_PROC_FS */
5108
5109#ifdef CONFIG_SYSCTL
5110
Linus Torvalds1da177e2005-04-16 15:20:36 -07005111static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005112int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005113 void __user *buffer, size_t *lenp, loff_t *ppos)
5114{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005115 struct net *net;
5116 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005117 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005118 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005119 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005120
5121 net = (struct net *)ctl->extra1;
5122 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005123 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5124 if (ret)
5125 return ret;
5126
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005127 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005128 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129}
5130
David Ahern7c6bb7d2018-10-11 20:17:21 -07005131static int zero;
5132static int one = 1;
5133
David Aherned792e22018-10-08 14:06:34 -07005134static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005135 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005136 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005137 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005138 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005139 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005140 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005141 },
5142 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005143 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005144 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005145 .maxlen = sizeof(int),
5146 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005147 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005148 },
5149 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005151 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005152 .maxlen = sizeof(int),
5153 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005154 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005155 },
5156 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005157 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005158 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005159 .maxlen = sizeof(int),
5160 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005161 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005162 },
5163 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005164 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005165 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005166 .maxlen = sizeof(int),
5167 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005168 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005169 },
5170 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005171 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005172 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005173 .maxlen = sizeof(int),
5174 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005175 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005176 },
5177 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005178 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005179 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 .maxlen = sizeof(int),
5181 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005182 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005183 },
5184 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005186 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
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_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 },
5191 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005193 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 .maxlen = sizeof(int),
5195 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005196 .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_ms",
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_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005205 {
5206 .procname = "skip_notify_on_dev_down",
5207 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5208 .maxlen = sizeof(int),
5209 .mode = 0644,
5210 .proc_handler = proc_dointvec,
5211 .extra1 = &zero,
5212 .extra2 = &one,
5213 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005214 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005215};
5216
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005217struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005218{
5219 struct ctl_table *table;
5220
5221 table = kmemdup(ipv6_route_table_template,
5222 sizeof(ipv6_route_table_template),
5223 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005224
5225 if (table) {
5226 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005227 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005228 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005229 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5230 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5231 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5232 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5233 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5234 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5235 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005236 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005237 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005238
5239 /* Don't export sysctls to unprivileged users */
5240 if (net->user_ns != &init_user_ns)
5241 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005242 }
5243
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005244 return table;
5245}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246#endif
5247
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005248static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005249{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005250 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005251
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005252 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5253 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005254
Eric Dumazetfc66f952010-10-08 06:37:34 +00005255 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5256 goto out_ip6_dst_ops;
5257
David Ahern421842e2018-04-17 17:33:18 -07005258 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5259 sizeof(*net->ipv6.fib6_null_entry),
5260 GFP_KERNEL);
5261 if (!net->ipv6.fib6_null_entry)
5262 goto out_ip6_dst_entries;
5263
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005264 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5265 sizeof(*net->ipv6.ip6_null_entry),
5266 GFP_KERNEL);
5267 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005268 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005269 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005270 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5271 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005272
5273#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005274 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005275 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5276 sizeof(*net->ipv6.ip6_prohibit_entry),
5277 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005278 if (!net->ipv6.ip6_prohibit_entry)
5279 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005280 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005281 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5282 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005283
5284 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5285 sizeof(*net->ipv6.ip6_blk_hole_entry),
5286 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005287 if (!net->ipv6.ip6_blk_hole_entry)
5288 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005289 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005290 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5291 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005292#endif
5293
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005294 net->ipv6.sysctl.flush_delay = 0;
5295 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5296 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5297 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5298 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5299 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5300 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5301 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005302 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005303
Benjamin Thery6891a342008-03-04 13:49:47 -08005304 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5305
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005306 ret = 0;
5307out:
5308 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005309
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005310#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5311out_ip6_prohibit_entry:
5312 kfree(net->ipv6.ip6_prohibit_entry);
5313out_ip6_null_entry:
5314 kfree(net->ipv6.ip6_null_entry);
5315#endif
David Ahern421842e2018-04-17 17:33:18 -07005316out_fib6_null_entry:
5317 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005318out_ip6_dst_entries:
5319 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005320out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005321 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005322}
5323
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005324static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005325{
David Ahern421842e2018-04-17 17:33:18 -07005326 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005327 kfree(net->ipv6.ip6_null_entry);
5328#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5329 kfree(net->ipv6.ip6_prohibit_entry);
5330 kfree(net->ipv6.ip6_blk_hole_entry);
5331#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005332 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005333}
5334
Thomas Grafd1896342012-06-18 12:08:33 +00005335static int __net_init ip6_route_net_init_late(struct net *net)
5336{
5337#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005338 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5339 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005340 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5341 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005342#endif
5343 return 0;
5344}
5345
5346static void __net_exit ip6_route_net_exit_late(struct net *net)
5347{
5348#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005349 remove_proc_entry("ipv6_route", net->proc_net);
5350 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005351#endif
5352}
5353
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005354static struct pernet_operations ip6_route_net_ops = {
5355 .init = ip6_route_net_init,
5356 .exit = ip6_route_net_exit,
5357};
5358
David S. Millerc3426b42012-06-09 16:27:05 -07005359static int __net_init ipv6_inetpeer_init(struct net *net)
5360{
5361 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5362
5363 if (!bp)
5364 return -ENOMEM;
5365 inet_peer_base_init(bp);
5366 net->ipv6.peers = bp;
5367 return 0;
5368}
5369
5370static void __net_exit ipv6_inetpeer_exit(struct net *net)
5371{
5372 struct inet_peer_base *bp = net->ipv6.peers;
5373
5374 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005375 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005376 kfree(bp);
5377}
5378
David S. Miller2b823f72012-06-09 19:00:16 -07005379static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005380 .init = ipv6_inetpeer_init,
5381 .exit = ipv6_inetpeer_exit,
5382};
5383
Thomas Grafd1896342012-06-18 12:08:33 +00005384static struct pernet_operations ip6_route_net_late_ops = {
5385 .init = ip6_route_net_init_late,
5386 .exit = ip6_route_net_exit_late,
5387};
5388
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005389static struct notifier_block ip6_route_dev_notifier = {
5390 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005391 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005392};
5393
WANG Cong2f460932017-05-03 22:07:31 -07005394void __init ip6_route_init_special_entries(void)
5395{
5396 /* Registering of the loopback is done before this portion of code,
5397 * the loopback reference in rt6_info will not be taken, do it
5398 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005399 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005400 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5401 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5402 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5403 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5404 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5405 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5406 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5407 #endif
5408}
5409
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005410int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005411{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005412 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005413 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005414
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005415 ret = -ENOMEM;
5416 ip6_dst_ops_template.kmem_cachep =
5417 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5418 SLAB_HWCACHE_ALIGN, NULL);
5419 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005420 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005421
Eric Dumazetfc66f952010-10-08 06:37:34 +00005422 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005423 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005424 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005425
David S. Millerc3426b42012-06-09 16:27:05 -07005426 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5427 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005428 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005429
David S. Miller7e52b332012-06-15 15:51:55 -07005430 ret = register_pernet_subsys(&ip6_route_net_ops);
5431 if (ret)
5432 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005433
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005434 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5435
David S. Millere8803b62012-06-16 01:12:19 -07005436 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005437 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005438 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005439
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005440 ret = xfrm6_init();
5441 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005442 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005443
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005444 ret = fib6_rules_init();
5445 if (ret)
5446 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005447
Thomas Grafd1896342012-06-18 12:08:33 +00005448 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5449 if (ret)
5450 goto fib6_rules_init;
5451
Florian Westphal16feebc2017-12-02 21:44:08 +01005452 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5453 inet6_rtm_newroute, NULL, 0);
5454 if (ret < 0)
5455 goto out_register_late_subsys;
5456
5457 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5458 inet6_rtm_delroute, NULL, 0);
5459 if (ret < 0)
5460 goto out_register_late_subsys;
5461
5462 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5463 inet6_rtm_getroute, NULL,
5464 RTNL_FLAG_DOIT_UNLOCKED);
5465 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005466 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005467
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005468 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005469 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005470 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005471
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005472 for_each_possible_cpu(cpu) {
5473 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5474
5475 INIT_LIST_HEAD(&ul->head);
5476 spin_lock_init(&ul->lock);
5477 }
5478
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005479out:
5480 return ret;
5481
Thomas Grafd1896342012-06-18 12:08:33 +00005482out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005483 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005484 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005485fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005486 fib6_rules_cleanup();
5487xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005488 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005489out_fib6_init:
5490 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005491out_register_subsys:
5492 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005493out_register_inetpeer:
5494 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005495out_dst_entries:
5496 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005497out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005498 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005499 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005500}
5501
5502void ip6_route_cleanup(void)
5503{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005504 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005505 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005506 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005507 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005508 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005509 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005510 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005511 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005512 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005513}