blob: e8c73b7782cdc5a2c4ca5ef50aca4c8d8556c7be [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Linux INET6 implementation
3 * FIB front-end.
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/* Changes:
15 *
16 * YOSHIFUJI Hideaki @USAGI
17 * reworked default router selection.
18 * - respect outgoing interface
19 * - select from (probably) reachable routers (i.e.
20 * routers in REACHABLE, STALE, DELAY or PROBE states).
21 * - always select the same router if it is (probably)
22 * reachable. otherwise, round-robin the list.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070023 * Ville Nuorvala
24 * Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 */
26
Joe Perchesf3213832012-05-15 14:11:53 +000027#define pr_fmt(fmt) "IPv6: " fmt
28
Randy Dunlap4fc268d2006-01-11 12:17:47 -080029#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/errno.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040031#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/types.h>
33#include <linux/times.h>
34#include <linux/socket.h>
35#include <linux/sockios.h>
36#include <linux/net.h>
37#include <linux/route.h>
38#include <linux/netdevice.h>
39#include <linux/in6.h>
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +090040#include <linux/mroute6.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/if_arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/proc_fs.h>
44#include <linux/seq_file.h>
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080045#include <linux/nsproxy.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090046#include <linux/slab.h>
Wei Wang35732d02017-10-06 12:05:57 -070047#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020048#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <net/snmp.h>
50#include <net/ipv6.h>
51#include <net/ip6_fib.h>
52#include <net/ip6_route.h>
53#include <net/ndisc.h>
54#include <net/addrconf.h>
55#include <net/tcp.h>
56#include <linux/rtnetlink.h>
57#include <net/dst.h>
Jiri Benc904af042015-08-20 13:56:31 +020058#include <net/dst_metadata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <net/xfrm.h>
Tom Tucker8d717402006-07-30 20:43:36 -070060#include <net/netevent.h>
Thomas Graf21713eb2006-08-15 00:35:24 -070061#include <net/netlink.h>
Nicolas Dichtel51ebd312012-10-22 03:42:09 +000062#include <net/nexthop.h>
Roopa Prabhu19e42e42015-07-21 10:43:48 +020063#include <net/lwtunnel.h>
Jiri Benc904af042015-08-20 13:56:31 +020064#include <net/ip_tunnels.h>
David Ahernca254492015-10-12 11:47:10 -070065#include <net/l3mdev.h>
Roopa Prabhueacb9382018-05-22 14:03:28 -070066#include <net/ip.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080067#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69#ifdef CONFIG_SYSCTL
70#include <linux/sysctl.h>
71#endif
72
David Ahern30d444d2018-05-23 17:08:48 -070073static int ip6_rt_type_to_error(u8 fib6_type);
74
75#define CREATE_TRACE_POINTS
76#include <trace/events/fib6.h>
77EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
78#undef CREATE_TRACE_POINTS
79
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020080enum rt6_nud_state {
Jiri Benc7e980562013-12-11 13:48:20 +010081 RT6_NUD_FAIL_HARD = -3,
82 RT6_NUD_FAIL_PROBE = -2,
83 RT6_NUD_FAIL_DO_RR = -1,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +020084 RT6_NUD_SUCCEED = 1
85};
86
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
David S. Miller0dbaee32010-12-13 12:52:14 -080088static unsigned int ip6_default_advmss(const struct dst_entry *dst);
Steffen Klassertebb762f2011-11-23 02:12:51 +000089static unsigned int ip6_mtu(const struct dst_entry *dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090static struct dst_entry *ip6_negative_advice(struct dst_entry *);
91static void ip6_dst_destroy(struct dst_entry *);
92static void ip6_dst_ifdown(struct dst_entry *,
93 struct net_device *dev, int how);
Daniel Lezcano569d3642008-01-18 03:56:57 -080094static int ip6_dst_gc(struct dst_ops *ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
96static int ip6_pkt_discard(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050097static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Kamala R7150aed2013-12-02 19:55:21 +053098static int ip6_pkt_prohibit(struct sk_buff *skb);
Eric W. Biedermanede20592015-10-07 16:48:47 -050099static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static void ip6_link_failure(struct sk_buff *skb);
David S. Miller6700c272012-07-17 03:29:28 -0700101static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
102 struct sk_buff *skb, u32 mtu);
103static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
104 struct sk_buff *skb);
David Ahern702cea52019-04-09 14:41:13 -0700105static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
106 int strict);
David Ahern8d1c8022018-04-17 17:33:26 -0700107static size_t rt6_nlmsg_size(struct fib6_info *rt);
David Ahernd4ead6b2018-04-17 17:33:16 -0700108static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -0700109 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -0700110 struct in6_addr *dest, struct in6_addr *src,
David Ahern16a16cd2017-02-02 12:37:11 -0800111 int iif, int type, u32 portid, u32 seq,
112 unsigned int flags);
David Ahern7e4b5122019-04-16 14:36:00 -0700113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -0700114 struct in6_addr *daddr,
115 struct in6_addr *saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800117#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -0700118static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000119 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700120 const struct in6_addr *gwaddr,
121 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +0000122 unsigned int pref);
David Ahern8d1c8022018-04-17 17:33:26 -0700123static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000124 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -0700125 const struct in6_addr *gwaddr,
126 struct net_device *dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800127#endif
128
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700129struct uncached_list {
130 spinlock_t lock;
131 struct list_head head;
132};
133
134static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
135
Xin Long510c3212018-02-14 19:06:02 +0800136void rt6_uncached_list_add(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700137{
138 struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
139
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700140 rt->rt6i_uncached_list = ul;
141
142 spin_lock_bh(&ul->lock);
143 list_add_tail(&rt->rt6i_uncached, &ul->head);
144 spin_unlock_bh(&ul->lock);
145}
146
Xin Long510c3212018-02-14 19:06:02 +0800147void rt6_uncached_list_del(struct rt6_info *rt)
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700148{
149 if (!list_empty(&rt->rt6i_uncached)) {
150 struct uncached_list *ul = rt->rt6i_uncached_list;
Wei Wang81eb8442017-10-06 12:06:11 -0700151 struct net *net = dev_net(rt->dst.dev);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700152
153 spin_lock_bh(&ul->lock);
154 list_del(&rt->rt6i_uncached);
Wei Wang81eb8442017-10-06 12:06:11 -0700155 atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700156 spin_unlock_bh(&ul->lock);
157 }
158}
159
160static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
161{
162 struct net_device *loopback_dev = net->loopback_dev;
163 int cpu;
164
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500165 if (dev == loopback_dev)
166 return;
167
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700168 for_each_possible_cpu(cpu) {
169 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
170 struct rt6_info *rt;
171
172 spin_lock_bh(&ul->lock);
173 list_for_each_entry(rt, &ul->head, rt6i_uncached) {
174 struct inet6_dev *rt_idev = rt->rt6i_idev;
175 struct net_device *rt_dev = rt->dst.dev;
176
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500177 if (rt_idev->dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700178 rt->rt6i_idev = in6_dev_get(loopback_dev);
179 in6_dev_put(rt_idev);
180 }
181
Eric W. Biedermane332bc62015-10-12 11:02:08 -0500182 if (rt_dev == dev) {
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700183 rt->dst.dev = loopback_dev;
184 dev_hold(rt->dst.dev);
185 dev_put(rt_dev);
186 }
187 }
188 spin_unlock_bh(&ul->lock);
189 }
190}
191
David Ahernf8a1b432018-04-17 17:33:21 -0700192static inline const void *choose_neigh_daddr(const struct in6_addr *p,
David S. Millerf894cbf2012-07-02 21:52:24 -0700193 struct sk_buff *skb,
194 const void *daddr)
David S. Miller39232972012-01-26 15:22:32 -0500195{
David S. Millera7563f32012-01-26 16:29:16 -0500196 if (!ipv6_addr_any(p))
David S. Miller39232972012-01-26 15:22:32 -0500197 return (const void *) p;
David S. Millerf894cbf2012-07-02 21:52:24 -0700198 else if (skb)
199 return &ipv6_hdr(skb)->daddr;
David S. Miller39232972012-01-26 15:22:32 -0500200 return daddr;
201}
202
David Ahernf8a1b432018-04-17 17:33:21 -0700203struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
204 struct net_device *dev,
205 struct sk_buff *skb,
206 const void *daddr)
David S. Millerd3aaeb32011-07-18 00:40:17 -0700207{
David S. Miller39232972012-01-26 15:22:32 -0500208 struct neighbour *n;
209
David Ahernf8a1b432018-04-17 17:33:21 -0700210 daddr = choose_neigh_daddr(gw, skb, daddr);
211 n = __ipv6_neigh_lookup(dev, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500212 if (n)
213 return n;
Stefano Brivio7adf3242019-01-02 13:29:27 +0100214
215 n = neigh_create(&nd_tbl, daddr, dev);
216 return IS_ERR(n) ? NULL : n;
David Ahernf8a1b432018-04-17 17:33:21 -0700217}
218
219static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
220 struct sk_buff *skb,
221 const void *daddr)
222{
223 const struct rt6_info *rt = container_of(dst, struct rt6_info, dst);
224
225 return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr);
David S. Millerf83c7792011-12-28 15:41:23 -0500226}
227
Julian Anastasov63fca652017-02-06 23:14:15 +0200228static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
229{
230 struct net_device *dev = dst->dev;
231 struct rt6_info *rt = (struct rt6_info *)dst;
232
David Ahernf8a1b432018-04-17 17:33:21 -0700233 daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr);
Julian Anastasov63fca652017-02-06 23:14:15 +0200234 if (!daddr)
235 return;
236 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
237 return;
238 if (ipv6_addr_is_multicast((const struct in6_addr *)daddr))
239 return;
240 __ipv6_confirm_neigh(dev, daddr);
241}
242
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -0800243static struct dst_ops ip6_dst_ops_template = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 .gc = ip6_dst_gc,
246 .gc_thresh = 1024,
247 .check = ip6_dst_check,
David S. Miller0dbaee32010-12-13 12:52:14 -0800248 .default_advmss = ip6_default_advmss,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000249 .mtu = ip6_mtu,
David Ahernd4ead6b2018-04-17 17:33:16 -0700250 .cow_metrics = dst_cow_metrics_generic,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .destroy = ip6_dst_destroy,
252 .ifdown = ip6_dst_ifdown,
253 .negative_advice = ip6_negative_advice,
254 .link_failure = ip6_link_failure,
255 .update_pmtu = ip6_rt_update_pmtu,
David S. Miller6e157b62012-07-12 00:05:02 -0700256 .redirect = rt6_do_redirect,
Eric W. Biederman9f8955c2015-10-07 16:48:39 -0500257 .local_out = __ip6_local_out,
David Ahernf8a1b432018-04-17 17:33:21 -0700258 .neigh_lookup = ip6_dst_neigh_lookup,
Julian Anastasov63fca652017-02-06 23:14:15 +0200259 .confirm_neigh = ip6_confirm_neigh,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Steffen Klassertebb762f2011-11-23 02:12:51 +0000262static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst)
Roland Dreierec831ea2011-01-31 13:16:00 -0800263{
Steffen Klassert618f9bc2011-11-23 02:13:31 +0000264 unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
265
266 return mtu ? : dst->dev->mtu;
Roland Dreierec831ea2011-01-31 13:16:00 -0800267}
268
David S. Miller6700c272012-07-17 03:29:28 -0700269static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
270 struct sk_buff *skb, u32 mtu)
David S. Miller14e50e52007-05-24 18:17:54 -0700271{
272}
273
David S. Miller6700c272012-07-17 03:29:28 -0700274static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk,
275 struct sk_buff *skb)
David S. Millerb587ee32012-07-12 00:39:24 -0700276{
277}
278
David S. Miller14e50e52007-05-24 18:17:54 -0700279static struct dst_ops ip6_dst_blackhole_ops = {
280 .family = AF_INET6,
David S. Miller14e50e52007-05-24 18:17:54 -0700281 .destroy = ip6_dst_destroy,
282 .check = ip6_dst_check,
Steffen Klassertebb762f2011-11-23 02:12:51 +0000283 .mtu = ip6_blackhole_mtu,
Eric Dumazet214f45c2011-02-18 11:39:01 -0800284 .default_advmss = ip6_default_advmss,
David S. Miller14e50e52007-05-24 18:17:54 -0700285 .update_pmtu = ip6_rt_blackhole_update_pmtu,
David S. Millerb587ee32012-07-12 00:39:24 -0700286 .redirect = ip6_rt_blackhole_redirect,
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -0700287 .cow_metrics = dst_cow_metrics_generic,
David Ahernf8a1b432018-04-17 17:33:21 -0700288 .neigh_lookup = ip6_dst_neigh_lookup,
David S. Miller14e50e52007-05-24 18:17:54 -0700289};
290
David S. Miller62fa8a82011-01-26 20:51:05 -0800291static const u32 ip6_template_metrics[RTAX_MAX] = {
Li RongQing14edd872012-10-24 14:01:18 +0800292 [RTAX_HOPLIMIT - 1] = 0,
David S. Miller62fa8a82011-01-26 20:51:05 -0800293};
294
David Ahern8d1c8022018-04-17 17:33:26 -0700295static const struct fib6_info fib6_null_entry_template = {
David Ahern93c2fb22018-04-18 15:38:59 -0700296 .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP),
297 .fib6_protocol = RTPROT_KERNEL,
298 .fib6_metric = ~(u32)0,
299 .fib6_ref = ATOMIC_INIT(1),
David Ahern421842e2018-04-17 17:33:18 -0700300 .fib6_type = RTN_UNREACHABLE,
301 .fib6_metrics = (struct dst_metrics *)&dst_default_metrics,
302};
303
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000304static const struct rt6_info ip6_null_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700305 .dst = {
306 .__refcnt = ATOMIC_INIT(1),
307 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000308 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700309 .error = -ENETUNREACH,
Changli Gaod8d1f302010-06-10 23:31:35 -0700310 .input = ip6_pkt_discard,
311 .output = ip6_pkt_discard_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 },
313 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314};
315
Thomas Graf101367c2006-08-04 03:39:02 -0700316#ifdef CONFIG_IPV6_MULTIPLE_TABLES
317
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000318static const struct rt6_info ip6_prohibit_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700319 .dst = {
320 .__refcnt = ATOMIC_INIT(1),
321 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000322 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700323 .error = -EACCES,
Changli Gaod8d1f302010-06-10 23:31:35 -0700324 .input = ip6_pkt_prohibit,
325 .output = ip6_pkt_prohibit_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700326 },
327 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700328};
329
Eric Dumazetfb0af4c2012-09-11 21:47:51 +0000330static const struct rt6_info ip6_blk_hole_entry_template = {
Changli Gaod8d1f302010-06-10 23:31:35 -0700331 .dst = {
332 .__refcnt = ATOMIC_INIT(1),
333 .__use = 1,
Nicolas Dichtel2c20cbd2012-09-10 22:09:47 +0000334 .obsolete = DST_OBSOLETE_FORCE_CHK,
Changli Gaod8d1f302010-06-10 23:31:35 -0700335 .error = -EINVAL,
Changli Gaod8d1f302010-06-10 23:31:35 -0700336 .input = dst_discard,
Eric W. Biedermanede20592015-10-07 16:48:47 -0500337 .output = dst_discard_out,
Thomas Graf101367c2006-08-04 03:39:02 -0700338 },
339 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
Thomas Graf101367c2006-08-04 03:39:02 -0700340};
341
342#endif
343
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700344static void rt6_info_init(struct rt6_info *rt)
345{
346 struct dst_entry *dst = &rt->dst;
347
348 memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700349 INIT_LIST_HEAD(&rt->rt6i_uncached);
350}
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352/* allocate dst with ip6_dst_ops */
David Ahern93531c62018-04-17 17:33:25 -0700353struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev,
354 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David S. Miller97bab732012-06-09 22:36:36 -0700356 struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
Wei Wangb2a9c0e2017-06-17 10:42:41 -0700357 1, DST_OBSOLETE_FORCE_CHK, flags);
David S. Millercf911662011-04-28 14:31:47 -0700358
Wei Wang81eb8442017-10-06 12:06:11 -0700359 if (rt) {
Martin KaFai Lauebfa45f2015-10-15 16:39:57 -0700360 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -0700361 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
362 }
Steffen Klassert81048912012-07-05 23:37:09 +0000363
David S. Millercf911662011-04-28 14:31:47 -0700364 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365}
David Ahern9ab179d2016-04-07 11:10:06 -0700366EXPORT_SYMBOL(ip6_dst_alloc);
Martin KaFai Laud52d3992015-05-22 20:56:06 -0700367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368static void ip6_dst_destroy(struct dst_entry *dst)
369{
370 struct rt6_info *rt = (struct rt6_info *)dst;
David Aherna68886a2018-04-20 15:38:02 -0700371 struct fib6_info *from;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700372 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
David Ahern1620a332018-10-04 20:07:54 -0700374 ip_dst_metrics_put(dst);
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -0700375 rt6_uncached_list_del(rt);
376
377 idev = rt->rt6i_idev;
David S. Miller38308472011-12-03 18:02:47 -0500378 if (idev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 rt->rt6i_idev = NULL;
380 in6_dev_put(idev);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900381 }
Gao feng1716a962012-04-06 00:13:10 +0000382
David Aherna68886a2018-04-20 15:38:02 -0700383 rcu_read_lock();
384 from = rcu_dereference(rt->from);
385 rcu_assign_pointer(rt->from, NULL);
David Ahern93531c62018-04-17 17:33:25 -0700386 fib6_info_release(from);
David Aherna68886a2018-04-20 15:38:02 -0700387 rcu_read_unlock();
David S. Millerb3419362010-11-30 12:27:11 -0800388}
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
391 int how)
392{
393 struct rt6_info *rt = (struct rt6_info *)dst;
394 struct inet6_dev *idev = rt->rt6i_idev;
Denis V. Lunev5a3e55d2007-12-07 00:38:10 -0800395 struct net_device *loopback_dev =
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900396 dev_net(dev)->loopback_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Wei Wange5645f52017-08-14 10:44:59 -0700398 if (idev && idev->dev != loopback_dev) {
399 struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev);
400 if (loopback_idev) {
401 rt->rt6i_idev = loopback_idev;
402 in6_dev_put(idev);
David S. Miller97cac082012-07-02 22:43:47 -0700403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405}
406
Martin KaFai Lau5973fb12015-11-11 11:51:07 -0800407static bool __rt6_check_expired(const struct rt6_info *rt)
408{
409 if (rt->rt6i_flags & RTF_EXPIRES)
410 return time_after(jiffies, rt->dst.expires);
411 else
412 return false;
413}
414
Eric Dumazeta50feda2012-05-18 18:57:34 +0000415static bool rt6_check_expired(const struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
David Aherna68886a2018-04-20 15:38:02 -0700417 struct fib6_info *from;
418
419 from = rcu_dereference(rt->from);
420
Gao feng1716a962012-04-06 00:13:10 +0000421 if (rt->rt6i_flags & RTF_EXPIRES) {
422 if (time_after(jiffies, rt->dst.expires))
Eric Dumazeta50feda2012-05-18 18:57:34 +0000423 return true;
David Aherna68886a2018-04-20 15:38:02 -0700424 } else if (from) {
Xin Long1e2ea8a2017-08-26 20:10:10 +0800425 return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
David Aherna68886a2018-04-20 15:38:02 -0700426 fib6_check_expired(from);
Gao feng1716a962012-04-06 00:13:10 +0000427 }
Eric Dumazeta50feda2012-05-18 18:57:34 +0000428 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
David Ahernb1d40992019-04-16 14:35:59 -0700431void fib6_select_path(const struct net *net, struct fib6_result *res,
432 struct flowi6 *fl6, int oif, bool have_oif_match,
433 const struct sk_buff *skb, int strict)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000434{
David Ahern8d1c8022018-04-17 17:33:26 -0700435 struct fib6_info *sibling, *next_sibling;
David Ahernb1d40992019-04-16 14:35:59 -0700436 struct fib6_info *match = res->f6i;
437
438 if (!match->fib6_nsiblings || have_oif_match)
439 goto out;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000440
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200441 /* We might have already computed the hash for ICMPv6 errors. In such
442 * case it will always be non-zero. Otherwise now is the time to do it.
443 */
444 if (!fl6->mp_hash)
David Ahernb4bac172018-03-02 08:32:18 -0800445 fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
Jakub Sitnickib673d6c2017-08-23 09:58:31 +0200446
David Ahernad1601a2019-03-27 20:53:56 -0700447 if (fl6->mp_hash <= atomic_read(&match->fib6_nh.fib_nh_upper_bound))
David Ahernb1d40992019-04-16 14:35:59 -0700448 goto out;
Ido Schimmelbbfcd772017-11-21 09:50:12 +0200449
David Ahern93c2fb22018-04-18 15:38:59 -0700450 list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
451 fib6_siblings) {
David Ahern702cea52019-04-09 14:41:13 -0700452 const struct fib6_nh *nh = &sibling->fib6_nh;
David Ahern5e670d82018-04-17 17:33:14 -0700453 int nh_upper_bound;
454
David Ahern702cea52019-04-09 14:41:13 -0700455 nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
David Ahern5e670d82018-04-17 17:33:14 -0700456 if (fl6->mp_hash > nh_upper_bound)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200457 continue;
David Ahern702cea52019-04-09 14:41:13 -0700458 if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0)
Ido Schimmel3d709f62018-01-09 16:40:27 +0200459 break;
460 match = sibling;
461 break;
462 }
463
David Ahernb1d40992019-04-16 14:35:59 -0700464out:
465 res->f6i = match;
466 res->nh = &match->fib6_nh;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +0000467}
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/*
Wei Wang66f5d6c2017-10-06 12:06:10 -0700470 * Route lookup. rcu_read_lock() should be held.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 */
472
David Ahern0c59d002019-04-09 14:41:18 -0700473static bool __rt6_device_match(struct net *net, const struct fib6_nh *nh,
474 const struct in6_addr *saddr, int oif, int flags)
475{
476 const struct net_device *dev;
477
478 if (nh->fib_nh_flags & RTNH_F_DEAD)
479 return false;
480
481 dev = nh->fib_nh_dev;
482 if (oif) {
483 if (dev->ifindex == oif)
484 return true;
485 } else {
486 if (ipv6_chk_addr(net, saddr, dev,
487 flags & RT6_LOOKUP_F_IFACE))
488 return true;
489 }
490
491 return false;
492}
493
David Ahern75ef7382019-04-16 14:36:07 -0700494static void rt6_device_match(struct net *net, struct fib6_result *res,
495 const struct in6_addr *saddr, int oif, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
David Ahern75ef7382019-04-16 14:36:07 -0700497 struct fib6_info *f6i = res->f6i;
498 struct fib6_info *spf6i;
499 struct fib6_nh *nh;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
David Ahern75ef7382019-04-16 14:36:07 -0700501 if (!oif && ipv6_addr_any(saddr)) {
502 nh = &f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700503 if (!(nh->fib_nh_flags & RTNH_F_DEAD))
504 goto out;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900505 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
David Ahern75ef7382019-04-16 14:36:07 -0700507 for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) {
508 nh = &spf6i->fib6_nh;
509 if (__rt6_device_match(net, nh, saddr, oif, flags)) {
510 res->f6i = spf6i;
David Ahern7d21fec2019-04-16 14:36:11 -0700511 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700512 }
513 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
David Ahern75ef7382019-04-16 14:36:07 -0700515 if (oif && flags & RT6_LOOKUP_F_IFACE) {
516 res->f6i = net->ipv6.fib6_null_entry;
David Ahern7d21fec2019-04-16 14:36:11 -0700517 nh = &res->f6i->fib6_nh;
518 goto out;
David Ahern75ef7382019-04-16 14:36:07 -0700519 }
520
David Ahern7d21fec2019-04-16 14:36:11 -0700521 nh = &f6i->fib6_nh;
522 if (nh->fib_nh_flags & RTNH_F_DEAD) {
David Ahern75ef7382019-04-16 14:36:07 -0700523 res->f6i = net->ipv6.fib6_null_entry;
David Ahern7d21fec2019-04-16 14:36:11 -0700524 nh = &res->f6i->fib6_nh;
David Ahern75ef7382019-04-16 14:36:07 -0700525 }
David Ahern7d21fec2019-04-16 14:36:11 -0700526out:
527 res->nh = nh;
528 res->fib6_type = res->f6i->fib6_type;
529 res->fib6_flags = res->f6i->fib6_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800532#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200533struct __rt6_probe_work {
534 struct work_struct work;
535 struct in6_addr target;
536 struct net_device *dev;
537};
538
539static void rt6_probe_deferred(struct work_struct *w)
540{
541 struct in6_addr mcaddr;
542 struct __rt6_probe_work *work =
543 container_of(w, struct __rt6_probe_work, work);
544
545 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800546 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200547 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100548 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200549}
550
David Aherncc3a86c2019-04-09 14:41:12 -0700551static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800552{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200553 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700554 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000555 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700556 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200557 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700558
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800559 /*
560 * Okay, this does not seem to be appropriate
561 * for now, however, we need to check if it
562 * is really so; aka Router Reachability Probing.
563 *
564 * Router Reachability Probe MUST be rate-limited
565 * to no more than one per minute.
566 */
David Aherncc3a86c2019-04-09 14:41:12 -0700567 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000568 return;
David Ahern5e670d82018-04-17 17:33:14 -0700569
David Aherncc3a86c2019-04-09 14:41:12 -0700570 nh_gw = &fib6_nh->fib_nh_gw6;
571 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000572 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200573 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700574 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000575 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700576 if (neigh->nud_state & NUD_VALID)
577 goto out;
578
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000579 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700580 if (!(neigh->nud_state & NUD_VALID) &&
581 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700582 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700583 work = kmalloc(sizeof(*work), GFP_ATOMIC);
584 if (work)
585 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200586 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000587 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700588 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200589 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700590 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000591 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700592
593 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700594 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700595 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700596 work->target = *nh_gw;
597 dev_hold(dev);
598 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700599 schedule_work(&work->work);
600 }
601
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700602out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000603 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800604}
605#else
David Aherncc3a86c2019-04-09 14:41:12 -0700606static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800607{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800608}
609#endif
610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800612 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 */
David Ahern1ba9a892019-04-09 14:41:10 -0700614static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200616 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700617 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000618
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000619 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700620 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
621 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000622 if (neigh) {
623 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800624 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200625 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800626#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000627 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200628 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100629 else
630 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800631#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000632 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200633 } else {
634 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100635 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000636 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000637 rcu_read_unlock_bh();
638
Paul Marksa5a81f02012-12-03 10:26:54 +0000639 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800640}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
David Ahern702cea52019-04-09 14:41:13 -0700642static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
643 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800644{
David Ahern6e1809a2019-04-09 14:41:11 -0700645 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900646
David Ahern6e1809a2019-04-09 14:41:11 -0700647 if (!oif || nh->fib_nh_dev->ifindex == oif)
648 m = 2;
649
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700650 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200651 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800652#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700653 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800654#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700655 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700656 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700657 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200658 if (n < 0)
659 return n;
660 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800661 return m;
662}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
David Ahern28679ed2019-04-09 14:41:14 -0700664static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
665 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800666{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200667 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700668 bool rc = false;
669 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400670
David Ahern28679ed2019-04-09 14:41:14 -0700671 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200672 goto out;
673
David Ahern28679ed2019-04-09 14:41:14 -0700674 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
675 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700676 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400677 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700678
David Ahern28679ed2019-04-09 14:41:14 -0700679 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100680 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200681 match_do_rr = true;
682 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100683 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700684 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700685 }
686
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200687 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700688 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200689
Jiri Benc7e980562013-12-11 13:48:20 +0100690 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200691 if (m > *mpri) {
692 *do_rr = match_do_rr;
693 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700694 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200695 }
David S. Millerf11e6652007-03-24 20:36:25 -0700696out:
David Ahern28679ed2019-04-09 14:41:14 -0700697 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700698}
699
David Ahernb7bc4b62019-04-16 14:36:08 -0700700static void __find_rr_leaf(struct fib6_info *f6i_start,
David Ahern30c15f02019-04-09 14:41:15 -0700701 struct fib6_info *nomatch, u32 metric,
David Ahernb7bc4b62019-04-16 14:36:08 -0700702 struct fib6_result *res, struct fib6_info **cont,
David Ahern30c15f02019-04-09 14:41:15 -0700703 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700704{
David Ahernb7bc4b62019-04-16 14:36:08 -0700705 struct fib6_info *f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700706
David Ahernb7bc4b62019-04-16 14:36:08 -0700707 for (f6i = f6i_start;
708 f6i && f6i != nomatch;
709 f6i = rcu_dereference(f6i->fib6_next)) {
David Ahern30c15f02019-04-09 14:41:15 -0700710 struct fib6_nh *nh;
711
David Ahernb7bc4b62019-04-16 14:36:08 -0700712 if (cont && f6i->fib6_metric != metric) {
713 *cont = f6i;
David Ahern30c15f02019-04-09 14:41:15 -0700714 return;
715 }
716
David Ahernb7bc4b62019-04-16 14:36:08 -0700717 if (fib6_check_expired(f6i))
David Ahern30c15f02019-04-09 14:41:15 -0700718 continue;
719
David Ahernb7bc4b62019-04-16 14:36:08 -0700720 nh = &f6i->fib6_nh;
721 if (find_match(nh, f6i->fib6_flags, oif, strict, mpri, do_rr)) {
722 res->f6i = f6i;
723 res->nh = nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700724 res->fib6_flags = f6i->fib6_flags;
725 res->fib6_type = f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700726 }
David Ahern30c15f02019-04-09 14:41:15 -0700727 }
728}
729
David Ahernb7bc4b62019-04-16 14:36:08 -0700730static void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf,
731 struct fib6_info *rr_head, int oif, int strict,
732 bool *do_rr, struct fib6_result *res)
David Ahern30c15f02019-04-09 14:41:15 -0700733{
David Ahernb7bc4b62019-04-16 14:36:08 -0700734 u32 metric = rr_head->fib6_metric;
735 struct fib6_info *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800736 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
David Ahernb7bc4b62019-04-16 14:36:08 -0700738 __find_rr_leaf(rr_head, NULL, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700739 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700740
David Ahernb7bc4b62019-04-16 14:36:08 -0700741 __find_rr_leaf(leaf, rr_head, metric, res, &cont,
David Ahern30c15f02019-04-09 14:41:15 -0700742 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700743
David Ahernb7bc4b62019-04-16 14:36:08 -0700744 if (res->f6i || !cont)
745 return;
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700746
David Ahernb7bc4b62019-04-16 14:36:08 -0700747 __find_rr_leaf(cont, NULL, metric, res, NULL,
David Ahern30c15f02019-04-09 14:41:15 -0700748 oif, strict, do_rr, &mpri);
David S. Millerf11e6652007-03-24 20:36:25 -0700749}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800750
David Ahernb7bc4b62019-04-16 14:36:08 -0700751static void rt6_select(struct net *net, struct fib6_node *fn, int oif,
752 struct fib6_result *res, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700753{
David Ahern8d1c8022018-04-17 17:33:26 -0700754 struct fib6_info *leaf = rcu_dereference(fn->leaf);
David Ahernb7bc4b62019-04-16 14:36:08 -0700755 struct fib6_info *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200756 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700757 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
David Ahernb7bc4b62019-04-16 14:36:08 -0700759 /* make sure this function or its helpers sets f6i */
760 res->f6i = NULL;
761
David Ahern421842e2018-04-17 17:33:18 -0700762 if (!leaf || leaf == net->ipv6.fib6_null_entry)
David Ahernb7bc4b62019-04-16 14:36:08 -0700763 goto out;
Wei Wang8d1040e2017-10-06 12:06:08 -0700764
Wei Wang66f5d6c2017-10-06 12:06:10 -0700765 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700766 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700767 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Wei Wang17ecf592017-10-06 12:06:09 -0700769 /* Double check to make sure fn is not an intermediate node
770 * and fn->leaf does not points to its child's leaf
771 * (This might happen if all routes under fn are deleted from
772 * the tree and fib6_repair_tree() is called on the node.)
773 */
David Ahern93c2fb22018-04-18 15:38:59 -0700774 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700775#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700776 if (rt0->fib6_src.plen)
777 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700778#endif
779 if (fn->fn_bit != key_plen)
David Ahernb7bc4b62019-04-16 14:36:08 -0700780 goto out;
Wei Wang17ecf592017-10-06 12:06:09 -0700781
David Ahernb7bc4b62019-04-16 14:36:08 -0700782 find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200783 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700784 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700785
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800786 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700787 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700788 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700789
Wei Wang66f5d6c2017-10-06 12:06:10 -0700790 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700791 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700792 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700793 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700794 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700795 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700796 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
798
David Ahernb7bc4b62019-04-16 14:36:08 -0700799out:
800 if (!res->f6i) {
801 res->f6i = net->ipv6.fib6_null_entry;
802 res->nh = &res->f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -0700803 res->fib6_flags = res->f6i->fib6_flags;
804 res->fib6_type = res->f6i->fib6_type;
David Ahernb7bc4b62019-04-16 14:36:08 -0700805 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806}
807
David Ahern85bd05d2019-04-16 14:36:01 -0700808static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700809{
David Ahern85bd05d2019-04-16 14:36:01 -0700810 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
811 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700812}
813
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800814#ifdef CONFIG_IPV6_ROUTE_INFO
815int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000816 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800817{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900818 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800819 struct route_info *rinfo = (struct route_info *) opt;
820 struct in6_addr prefix_buf, *prefix;
821 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900822 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700823 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800824
825 if (len < sizeof(struct route_info)) {
826 return -EINVAL;
827 }
828
829 /* Sanity check for prefix_len and length */
830 if (rinfo->length > 3) {
831 return -EINVAL;
832 } else if (rinfo->prefix_len > 128) {
833 return -EINVAL;
834 } else if (rinfo->prefix_len > 64) {
835 if (rinfo->length < 2) {
836 return -EINVAL;
837 }
838 } else if (rinfo->prefix_len > 0) {
839 if (rinfo->length < 1) {
840 return -EINVAL;
841 }
842 }
843
844 pref = rinfo->route_pref;
845 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000846 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800847
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900848 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800849
850 if (rinfo->length == 3)
851 prefix = (struct in6_addr *)rinfo->prefix;
852 else {
853 /* this function is safe */
854 ipv6_addr_prefix(&prefix_buf,
855 (struct in6_addr *)rinfo->prefix,
856 rinfo->prefix_len);
857 prefix = &prefix_buf;
858 }
859
Duan Jiongf104a562013-11-08 09:56:53 +0800860 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700861 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800862 else
863 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700864 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800865
866 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700867 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800868 rt = NULL;
869 }
870
871 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700872 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
873 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800874 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700875 rt->fib6_flags = RTF_ROUTEINFO |
876 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800877
878 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000879 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700880 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000881 else
David Ahern14895682018-04-17 17:33:17 -0700882 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000883
David Ahern93531c62018-04-17 17:33:25 -0700884 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800885 }
886 return 0;
887}
888#endif
889
David Ahernae90d862018-04-17 17:33:12 -0700890/*
891 * Misc support functions
892 */
893
894/* called with rcu_lock held */
David Ahern0d161582019-04-16 14:36:04 -0700895static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -0700896{
David Ahern0d161582019-04-16 14:36:04 -0700897 struct net_device *dev = res->nh->fib_nh_dev;
David Ahernae90d862018-04-17 17:33:12 -0700898
David Ahern7d21fec2019-04-16 14:36:11 -0700899 if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700900 /* for copies of local routes, dst->dev needs to be the
901 * device if it is a master device, the master device if
902 * device is enslaved, and the loopback as the default
903 */
904 if (netif_is_l3_slave(dev) &&
David Ahern7d21fec2019-04-16 14:36:11 -0700905 !rt6_need_strict(&res->f6i->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700906 dev = l3mdev_master_dev_rcu(dev);
907 else if (!netif_is_l3_master(dev))
908 dev = dev_net(dev)->loopback_dev;
909 /* last case is netif_is_l3_master(dev) is true in which
910 * case we want dev returned to be dev
911 */
912 }
913
914 return dev;
915}
916
David Ahern6edb3c92018-04-17 17:33:15 -0700917static const int fib6_prop[RTN_MAX + 1] = {
918 [RTN_UNSPEC] = 0,
919 [RTN_UNICAST] = 0,
920 [RTN_LOCAL] = 0,
921 [RTN_BROADCAST] = 0,
922 [RTN_ANYCAST] = 0,
923 [RTN_MULTICAST] = 0,
924 [RTN_BLACKHOLE] = -EINVAL,
925 [RTN_UNREACHABLE] = -EHOSTUNREACH,
926 [RTN_PROHIBIT] = -EACCES,
927 [RTN_THROW] = -EAGAIN,
928 [RTN_NAT] = -EINVAL,
929 [RTN_XRESOLVE] = -EINVAL,
930};
931
932static int ip6_rt_type_to_error(u8 fib6_type)
933{
934 return fib6_prop[fib6_type];
935}
936
David Ahern8d1c8022018-04-17 17:33:26 -0700937static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700938{
939 unsigned short flags = 0;
940
941 if (rt->dst_nocount)
942 flags |= DST_NOCOUNT;
943 if (rt->dst_nopolicy)
944 flags |= DST_NOPOLICY;
945 if (rt->dst_host)
946 flags |= DST_HOST;
947
948 return flags;
949}
950
David Ahern7d21fec2019-04-16 14:36:11 -0700951static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type)
David Ahern6edb3c92018-04-17 17:33:15 -0700952{
David Ahern7d21fec2019-04-16 14:36:11 -0700953 rt->dst.error = ip6_rt_type_to_error(fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -0700954
David Ahern7d21fec2019-04-16 14:36:11 -0700955 switch (fib6_type) {
David Ahern6edb3c92018-04-17 17:33:15 -0700956 case RTN_BLACKHOLE:
957 rt->dst.output = dst_discard_out;
958 rt->dst.input = dst_discard;
959 break;
960 case RTN_PROHIBIT:
961 rt->dst.output = ip6_pkt_prohibit_out;
962 rt->dst.input = ip6_pkt_prohibit;
963 break;
964 case RTN_THROW:
965 case RTN_UNREACHABLE:
966 default:
967 rt->dst.output = ip6_pkt_discard_out;
968 rt->dst.input = ip6_pkt_discard;
969 break;
970 }
971}
972
David Ahern0d161582019-04-16 14:36:04 -0700973static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
David Ahern6edb3c92018-04-17 17:33:15 -0700974{
David Ahern7d21fec2019-04-16 14:36:11 -0700975 struct fib6_info *f6i = res->f6i;
David Ahern0d161582019-04-16 14:36:04 -0700976
David Ahern7d21fec2019-04-16 14:36:11 -0700977 if (res->fib6_flags & RTF_REJECT) {
978 ip6_rt_init_dst_reject(rt, res->fib6_type);
David Ahern6edb3c92018-04-17 17:33:15 -0700979 return;
980 }
981
982 rt->dst.error = 0;
983 rt->dst.output = ip6_output;
984
David Ahern7d21fec2019-04-16 14:36:11 -0700985 if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700986 rt->dst.input = ip6_input;
David Ahern7d21fec2019-04-16 14:36:11 -0700987 } else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700988 rt->dst.input = ip6_mc_input;
989 } else {
990 rt->dst.input = ip6_forward;
991 }
992
David Ahern0d161582019-04-16 14:36:04 -0700993 if (res->nh->fib_nh_lws) {
994 rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700995 lwtunnel_set_redirect(&rt->dst);
996 }
997
998 rt->dst.lastuse = jiffies;
999}
1000
Wei Wange873e4b2018-07-21 20:56:32 -07001001/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -07001002static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -07001003{
David Ahernae90d862018-04-17 17:33:12 -07001004 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -07001005 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -07001006 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -07001007}
1008
David Ahern0d161582019-04-16 14:36:04 -07001009/* Caller must already hold reference to f6i in result */
1010static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -07001011{
David Ahern0d161582019-04-16 14:36:04 -07001012 const struct fib6_nh *nh = res->nh;
1013 const struct net_device *dev = nh->fib_nh_dev;
1014 struct fib6_info *f6i = res->f6i;
David Aherndcd1f572018-04-18 15:39:05 -07001015
David Ahern0d161582019-04-16 14:36:04 -07001016 ip6_rt_init_dst(rt, res);
David Ahern6edb3c92018-04-17 17:33:15 -07001017
David Ahern0d161582019-04-16 14:36:04 -07001018 rt->rt6i_dst = f6i->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -07001019 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern7d21fec2019-04-16 14:36:11 -07001020 rt->rt6i_flags = res->fib6_flags;
David Ahern0d161582019-04-16 14:36:04 -07001021 if (nh->fib_nh_gw_family) {
1022 rt->rt6i_gateway = nh->fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -07001023 rt->rt6i_flags |= RTF_GATEWAY;
1024 }
David Ahern0d161582019-04-16 14:36:04 -07001025 rt6_set_from(rt, f6i);
David Ahernae90d862018-04-17 17:33:12 -07001026#ifdef CONFIG_IPV6_SUBTREES
David Ahern0d161582019-04-16 14:36:04 -07001027 rt->rt6i_src = f6i->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001028#endif
David Ahernae90d862018-04-17 17:33:12 -07001029}
1030
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001031static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1032 struct in6_addr *saddr)
1033{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001034 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001035 while (1) {
1036 if (fn->fn_flags & RTN_TL_ROOT)
1037 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001038 pn = rcu_dereference(fn->parent);
1039 sn = FIB6_SUBTREE(pn);
1040 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001041 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001042 else
1043 fn = pn;
1044 if (fn->fn_flags & RTN_RTINFO)
1045 return fn;
1046 }
1047}
Thomas Grafc71099a2006-08-04 23:20:06 -07001048
David Ahern10585b42019-03-20 09:24:50 -07001049static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001050{
1051 struct rt6_info *rt = *prt;
1052
1053 if (dst_hold_safe(&rt->dst))
1054 return true;
David Ahern10585b42019-03-20 09:24:50 -07001055 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001056 rt = net->ipv6.ip6_null_entry;
1057 dst_hold(&rt->dst);
1058 } else {
1059 rt = NULL;
1060 }
1061 *prt = rt;
1062 return false;
1063}
1064
David Aherndec9b0e2018-04-17 17:33:19 -07001065/* called with rcu_lock held */
David Ahern9b6b35a2019-04-16 14:36:02 -07001066static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
David Aherndec9b0e2018-04-17 17:33:19 -07001067{
David Ahern9b6b35a2019-04-16 14:36:02 -07001068 struct net_device *dev = res->nh->fib_nh_dev;
1069 struct fib6_info *f6i = res->f6i;
1070 unsigned short flags;
David Aherndec9b0e2018-04-17 17:33:19 -07001071 struct rt6_info *nrt;
1072
David Ahern9b6b35a2019-04-16 14:36:02 -07001073 if (!fib6_info_hold_safe(f6i))
Xin Long1c87e792019-03-20 14:45:48 +08001074 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001075
David Ahern9b6b35a2019-04-16 14:36:02 -07001076 flags = fib6_info_dst_flags(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001077 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001078 if (!nrt) {
David Ahern9b6b35a2019-04-16 14:36:02 -07001079 fib6_info_release(f6i);
Xin Long1c87e792019-03-20 14:45:48 +08001080 goto fallback;
1081 }
David Aherndec9b0e2018-04-17 17:33:19 -07001082
David Ahern0d161582019-04-16 14:36:04 -07001083 ip6_rt_copy_init(nrt, res);
Xin Long1c87e792019-03-20 14:45:48 +08001084 return nrt;
1085
1086fallback:
1087 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1088 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001089 return nrt;
1090}
1091
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001092static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1093 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001094 struct flowi6 *fl6,
1095 const struct sk_buff *skb,
1096 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097{
David Ahernb1d40992019-04-16 14:35:59 -07001098 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001100 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
David Ahernb6cdbc82018-03-29 17:44:57 -07001102 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1103 flags &= ~RT6_LOOKUP_F_IFACE;
1104
Wei Wang66f5d6c2017-10-06 12:06:10 -07001105 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001106 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001107restart:
David Ahernb1d40992019-04-16 14:35:59 -07001108 res.f6i = rcu_dereference(fn->leaf);
1109 if (!res.f6i)
1110 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001111 else
David Ahern75ef7382019-04-16 14:36:07 -07001112 rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif,
1113 flags);
David Ahernaf52a522019-04-09 14:41:16 -07001114
David Ahernb1d40992019-04-16 14:35:59 -07001115 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001116 fn = fib6_backtrack(fn, &fl6->saddr);
1117 if (fn)
1118 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001119
1120 rt = net->ipv6.ip6_null_entry;
1121 dst_hold(&rt->dst);
1122 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001123 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001124
David Ahernb1d40992019-04-16 14:35:59 -07001125 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1126 fl6->flowi6_oif != 0, skb, flags);
1127
David S. Miller4c9483b2011-03-12 16:22:43 -05001128 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001129 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001130 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001131 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001132 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001133 } else {
David Ahern9b6b35a2019-04-16 14:36:02 -07001134 rt = ip6_create_rt_rcu(&res);
David Aherndec9b0e2018-04-17 17:33:19 -07001135 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001136
David Ahernaf52a522019-04-09 14:41:16 -07001137out:
David Ahern8ff2e5b2019-04-16 14:36:09 -07001138 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001139
Wei Wang66f5d6c2017-10-06 12:06:10 -07001140 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001141
Thomas Grafc71099a2006-08-04 23:20:06 -07001142 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001143}
1144
Ian Morris67ba4152014-08-24 21:53:10 +01001145struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001146 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001147{
David Ahernb75cc8f2018-03-02 08:32:17 -08001148 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001149}
1150EXPORT_SYMBOL_GPL(ip6_route_lookup);
1151
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001152struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001153 const struct in6_addr *saddr, int oif,
1154 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001155{
David S. Miller4c9483b2011-03-12 16:22:43 -05001156 struct flowi6 fl6 = {
1157 .flowi6_oif = oif,
1158 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001159 };
1160 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001161 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001162
Thomas Grafadaa70b2006-10-13 15:01:03 -07001163 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001164 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001165 flags |= RT6_LOOKUP_F_HAS_SADDR;
1166 }
1167
David Ahernb75cc8f2018-03-02 08:32:17 -08001168 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001169 if (dst->error == 0)
1170 return (struct rt6_info *) dst;
1171
1172 dst_release(dst);
1173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 return NULL;
1175}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001176EXPORT_SYMBOL(rt6_lookup);
1177
Thomas Grafc71099a2006-08-04 23:20:06 -07001178/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001179 * It takes new route entry, the addition fails by any reason the
1180 * route is released.
1181 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 */
1183
David Ahern8d1c8022018-04-17 17:33:26 -07001184static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001185 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186{
1187 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001188 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
David Ahern93c2fb22018-04-18 15:38:59 -07001190 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001191 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001192 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001193 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
1195 return err;
1196}
1197
David Ahern8d1c8022018-04-17 17:33:26 -07001198int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001199{
David Ahernafb1d4b52018-04-17 17:33:11 -07001200 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001201
David Ahernd4ead6b2018-04-17 17:33:16 -07001202 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001203}
1204
David Ahern85bd05d2019-04-16 14:36:01 -07001205static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001206 const struct in6_addr *daddr,
1207 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208{
David Ahern85bd05d2019-04-16 14:36:01 -07001209 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001210 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 struct rt6_info *rt;
1212
1213 /*
1214 * Clone the route.
1215 */
1216
David Ahern85bd05d2019-04-16 14:36:01 -07001217 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001218 return NULL;
1219
David Ahern0d161582019-04-16 14:36:04 -07001220 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001221 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001222 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001223 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001224 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001225 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001226
David Ahern0d161582019-04-16 14:36:04 -07001227 ip6_rt_copy_init(rt, res);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001228 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001229 rt->dst.flags |= DST_HOST;
1230 rt->rt6i_dst.addr = *daddr;
1231 rt->rt6i_dst.plen = 128;
1232
David Ahern85bd05d2019-04-16 14:36:01 -07001233 if (!rt6_is_gw_or_nonexthop(res)) {
1234 if (f6i->fib6_dst.plen != 128 &&
1235 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001236 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001238 if (rt->rt6i_src.plen && saddr) {
1239 rt->rt6i_src.addr = *saddr;
1240 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001241 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001242#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001243 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001245 return rt;
1246}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
David Aherndb3fede2019-04-16 14:36:03 -07001248static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001249{
David Aherndb3fede2019-04-16 14:36:03 -07001250 struct fib6_info *f6i = res->f6i;
1251 unsigned short flags = fib6_info_dst_flags(f6i);
David Ahern4832c302017-08-17 12:17:20 -07001252 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001253 struct rt6_info *pcpu_rt;
1254
David Aherndb3fede2019-04-16 14:36:03 -07001255 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001256 return NULL;
1257
David Ahern4832c302017-08-17 12:17:20 -07001258 rcu_read_lock();
David Ahern0d161582019-04-16 14:36:04 -07001259 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001260 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001261 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001262 if (!pcpu_rt) {
David Aherndb3fede2019-04-16 14:36:03 -07001263 fib6_info_release(f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001264 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001265 }
David Ahern0d161582019-04-16 14:36:04 -07001266 ip6_rt_copy_init(pcpu_rt, res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001267 pcpu_rt->rt6i_flags |= RTF_PCPU;
1268 return pcpu_rt;
1269}
1270
Wei Wang66f5d6c2017-10-06 12:06:10 -07001271/* It should be called with rcu_read_lock() acquired */
David Aherndb3fede2019-04-16 14:36:03 -07001272static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001273{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001274 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001275
David Aherndb3fede2019-04-16 14:36:03 -07001276 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001277 pcpu_rt = *p;
1278
David Ahernd4ead6b2018-04-17 17:33:16 -07001279 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001280 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001281
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001282 return pcpu_rt;
1283}
1284
David Ahernafb1d4b52018-04-17 17:33:11 -07001285static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Aherndb3fede2019-04-16 14:36:03 -07001286 const struct fib6_result *res)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001287{
1288 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001289
David Aherndb3fede2019-04-16 14:36:03 -07001290 pcpu_rt = ip6_rt_pcpu_alloc(res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001291 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001292 dst_hold(&net->ipv6.ip6_null_entry->dst);
1293 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001294 }
1295
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001296 dst_hold(&pcpu_rt->dst);
David Aherndb3fede2019-04-16 14:36:03 -07001297 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Wei Wanga94b9362017-10-06 12:06:04 -07001298 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001299 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001300
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001301 return pcpu_rt;
1302}
1303
Wei Wang35732d02017-10-06 12:05:57 -07001304/* exception hash table implementation
1305 */
1306static DEFINE_SPINLOCK(rt6_exception_lock);
1307
1308/* Remove rt6_ex from hash table and free the memory
1309 * Caller must hold rt6_exception_lock
1310 */
1311static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1312 struct rt6_exception *rt6_ex)
1313{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001314 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001315 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001316
Wei Wang35732d02017-10-06 12:05:57 -07001317 if (!bucket || !rt6_ex)
1318 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001319
1320 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001321 net->ipv6.rt6_stats->fib_rt_cache--;
1322
1323 /* purge completely the exception to allow releasing the held resources:
1324 * some [sk] cache may keep the dst around for unlimited time
1325 */
1326 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1327 lockdep_is_held(&rt6_exception_lock));
1328 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1329 fib6_info_release(from);
1330 dst_dev_put(&rt6_ex->rt6i->dst);
1331
Wei Wang35732d02017-10-06 12:05:57 -07001332 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001333 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001334 kfree_rcu(rt6_ex, rcu);
1335 WARN_ON_ONCE(!bucket->depth);
1336 bucket->depth--;
1337}
1338
1339/* Remove oldest rt6_ex in bucket and free the memory
1340 * Caller must hold rt6_exception_lock
1341 */
1342static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1343{
1344 struct rt6_exception *rt6_ex, *oldest = NULL;
1345
1346 if (!bucket)
1347 return;
1348
1349 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1350 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1351 oldest = rt6_ex;
1352 }
1353 rt6_remove_exception(bucket, oldest);
1354}
1355
1356static u32 rt6_exception_hash(const struct in6_addr *dst,
1357 const struct in6_addr *src)
1358{
1359 static u32 seed __read_mostly;
1360 u32 val;
1361
1362 net_get_random_once(&seed, sizeof(seed));
1363 val = jhash(dst, sizeof(*dst), seed);
1364
1365#ifdef CONFIG_IPV6_SUBTREES
1366 if (src)
1367 val = jhash(src, sizeof(*src), val);
1368#endif
1369 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1370}
1371
1372/* Helper function to find the cached rt in the hash table
1373 * and update bucket pointer to point to the bucket for this
1374 * (daddr, saddr) pair
1375 * Caller must hold rt6_exception_lock
1376 */
1377static struct rt6_exception *
1378__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1379 const struct in6_addr *daddr,
1380 const struct in6_addr *saddr)
1381{
1382 struct rt6_exception *rt6_ex;
1383 u32 hval;
1384
1385 if (!(*bucket) || !daddr)
1386 return NULL;
1387
1388 hval = rt6_exception_hash(daddr, saddr);
1389 *bucket += hval;
1390
1391 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1392 struct rt6_info *rt6 = rt6_ex->rt6i;
1393 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1394
1395#ifdef CONFIG_IPV6_SUBTREES
1396 if (matched && saddr)
1397 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1398#endif
1399 if (matched)
1400 return rt6_ex;
1401 }
1402 return NULL;
1403}
1404
1405/* Helper function to find the cached rt in the hash table
1406 * and update bucket pointer to point to the bucket for this
1407 * (daddr, saddr) pair
1408 * Caller must hold rcu_read_lock()
1409 */
1410static struct rt6_exception *
1411__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1412 const struct in6_addr *daddr,
1413 const struct in6_addr *saddr)
1414{
1415 struct rt6_exception *rt6_ex;
1416 u32 hval;
1417
1418 WARN_ON_ONCE(!rcu_read_lock_held());
1419
1420 if (!(*bucket) || !daddr)
1421 return NULL;
1422
1423 hval = rt6_exception_hash(daddr, saddr);
1424 *bucket += hval;
1425
1426 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1427 struct rt6_info *rt6 = rt6_ex->rt6i;
1428 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1429
1430#ifdef CONFIG_IPV6_SUBTREES
1431 if (matched && saddr)
1432 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1433#endif
1434 if (matched)
1435 return rt6_ex;
1436 }
1437 return NULL;
1438}
1439
David Ahernb748f262019-04-16 14:36:06 -07001440static unsigned int fib6_mtu(const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001441{
David Ahernb748f262019-04-16 14:36:06 -07001442 const struct fib6_nh *nh = res->nh;
David Ahernd4ead6b2018-04-17 17:33:16 -07001443 unsigned int mtu;
1444
David Ahernb748f262019-04-16 14:36:06 -07001445 if (res->f6i->fib6_pmtu) {
1446 mtu = res->f6i->fib6_pmtu;
David Aherndcd1f572018-04-18 15:39:05 -07001447 } else {
David Ahernb748f262019-04-16 14:36:06 -07001448 struct net_device *dev = nh->fib_nh_dev;
David Aherndcd1f572018-04-18 15:39:05 -07001449 struct inet6_dev *idev;
1450
1451 rcu_read_lock();
1452 idev = __in6_dev_get(dev);
1453 mtu = idev->cnf.mtu6;
1454 rcu_read_unlock();
1455 }
1456
David Ahernd4ead6b2018-04-17 17:33:16 -07001457 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1458
David Ahernb748f262019-04-16 14:36:06 -07001459 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001460}
1461
Wei Wang35732d02017-10-06 12:05:57 -07001462static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern5012f0a2019-04-16 14:36:05 -07001463 const struct fib6_result *res)
Wei Wang35732d02017-10-06 12:05:57 -07001464{
David Ahern5e670d82018-04-17 17:33:14 -07001465 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001466 struct rt6_exception_bucket *bucket;
1467 struct in6_addr *src_key = NULL;
1468 struct rt6_exception *rt6_ex;
David Ahern5012f0a2019-04-16 14:36:05 -07001469 struct fib6_info *f6i = res->f6i;
Wei Wang35732d02017-10-06 12:05:57 -07001470 int err = 0;
1471
Wei Wang35732d02017-10-06 12:05:57 -07001472 spin_lock_bh(&rt6_exception_lock);
1473
David Ahern5012f0a2019-04-16 14:36:05 -07001474 if (f6i->exception_bucket_flushed) {
Wei Wang35732d02017-10-06 12:05:57 -07001475 err = -EINVAL;
1476 goto out;
1477 }
1478
David Ahern5012f0a2019-04-16 14:36:05 -07001479 bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket,
Wei Wang35732d02017-10-06 12:05:57 -07001480 lockdep_is_held(&rt6_exception_lock));
1481 if (!bucket) {
1482 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1483 GFP_ATOMIC);
1484 if (!bucket) {
1485 err = -ENOMEM;
1486 goto out;
1487 }
David Ahern5012f0a2019-04-16 14:36:05 -07001488 rcu_assign_pointer(f6i->rt6i_exception_bucket, bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001489 }
1490
1491#ifdef CONFIG_IPV6_SUBTREES
David Ahern5012f0a2019-04-16 14:36:05 -07001492 /* fib6_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001493 * and exception table is indexed by a hash of
David Ahern5012f0a2019-04-16 14:36:05 -07001494 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001495 * Otherwise, the exception table is indexed by
David Ahern5012f0a2019-04-16 14:36:05 -07001496 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001497 */
David Ahern5012f0a2019-04-16 14:36:05 -07001498 if (f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001499 src_key = &nrt->rt6i_src.addr;
1500#endif
David Ahern5012f0a2019-04-16 14:36:05 -07001501 /* rt6_mtu_change() might lower mtu on f6i.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001502 * Only insert this exception route if its mtu
David Ahern5012f0a2019-04-16 14:36:05 -07001503 * is less than f6i's mtu value.
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001504 */
David Ahernb748f262019-04-16 14:36:06 -07001505 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001506 err = -EINVAL;
1507 goto out;
1508 }
Wei Wang60006a42017-10-06 12:05:58 -07001509
Wei Wang35732d02017-10-06 12:05:57 -07001510 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1511 src_key);
1512 if (rt6_ex)
1513 rt6_remove_exception(bucket, rt6_ex);
1514
1515 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1516 if (!rt6_ex) {
1517 err = -ENOMEM;
1518 goto out;
1519 }
1520 rt6_ex->rt6i = nrt;
1521 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001522 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1523 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001524 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001525
1526 if (bucket->depth > FIB6_MAX_DEPTH)
1527 rt6_exception_remove_oldest(bucket);
1528
1529out:
1530 spin_unlock_bh(&rt6_exception_lock);
1531
1532 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001533 if (!err) {
David Ahern5012f0a2019-04-16 14:36:05 -07001534 spin_lock_bh(&f6i->fib6_table->tb6_lock);
1535 fib6_update_sernum(net, f6i);
1536 spin_unlock_bh(&f6i->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001537 fib6_force_start_gc(net);
1538 }
Wei Wang35732d02017-10-06 12:05:57 -07001539
1540 return err;
1541}
1542
David Ahern8d1c8022018-04-17 17:33:26 -07001543void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001544{
1545 struct rt6_exception_bucket *bucket;
1546 struct rt6_exception *rt6_ex;
1547 struct hlist_node *tmp;
1548 int i;
1549
1550 spin_lock_bh(&rt6_exception_lock);
1551 /* Prevent rt6_insert_exception() to recreate the bucket list */
1552 rt->exception_bucket_flushed = 1;
1553
1554 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1555 lockdep_is_held(&rt6_exception_lock));
1556 if (!bucket)
1557 goto out;
1558
1559 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1560 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1561 rt6_remove_exception(bucket, rt6_ex);
1562 WARN_ON_ONCE(bucket->depth);
1563 bucket++;
1564 }
1565
1566out:
1567 spin_unlock_bh(&rt6_exception_lock);
1568}
1569
1570/* Find cached rt in the hash table inside passed in rt
1571 * Caller has to hold rcu_read_lock()
1572 */
David Ahern7e4b5122019-04-16 14:36:00 -07001573static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -07001574 struct in6_addr *daddr,
1575 struct in6_addr *saddr)
1576{
1577 struct rt6_exception_bucket *bucket;
1578 struct in6_addr *src_key = NULL;
1579 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001580 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001581
David Ahern7e4b5122019-04-16 14:36:00 -07001582 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001583
1584#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001585 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001586 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001587 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001588 * Otherwise, the exception table is indexed by
David Ahern7e4b5122019-04-16 14:36:00 -07001589 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001590 */
David Ahern7e4b5122019-04-16 14:36:00 -07001591 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001592 src_key = saddr;
1593#endif
1594 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1595
1596 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001597 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001598
David Ahern7e4b5122019-04-16 14:36:00 -07001599 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001600}
1601
1602/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001603static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001604{
Wei Wang35732d02017-10-06 12:05:57 -07001605 struct rt6_exception_bucket *bucket;
1606 struct in6_addr *src_key = NULL;
1607 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001608 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001609 int err;
1610
Eric Dumazet091311d2018-04-24 09:22:49 -07001611 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001612 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001613 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001614 return -EINVAL;
1615
1616 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1617 return -ENOENT;
1618
1619 spin_lock_bh(&rt6_exception_lock);
1620 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1621 lockdep_is_held(&rt6_exception_lock));
1622#ifdef CONFIG_IPV6_SUBTREES
1623 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1624 * and exception table is indexed by a hash of
1625 * both rt6i_dst and rt6i_src.
1626 * Otherwise, the exception table is indexed by
1627 * a hash of only rt6i_dst.
1628 */
David Ahern93c2fb22018-04-18 15:38:59 -07001629 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001630 src_key = &rt->rt6i_src.addr;
1631#endif
1632 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1633 &rt->rt6i_dst.addr,
1634 src_key);
1635 if (rt6_ex) {
1636 rt6_remove_exception(bucket, rt6_ex);
1637 err = 0;
1638 } else {
1639 err = -ENOENT;
1640 }
1641
1642 spin_unlock_bh(&rt6_exception_lock);
1643 return err;
1644}
1645
1646/* Find rt6_ex which contains the passed in rt cache and
1647 * refresh its stamp
1648 */
1649static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1650{
Wei Wang35732d02017-10-06 12:05:57 -07001651 struct rt6_exception_bucket *bucket;
1652 struct in6_addr *src_key = NULL;
1653 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001654 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001655
1656 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001657 from = rcu_dereference(rt->from);
1658 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1659 goto unlock;
1660
Wei Wang35732d02017-10-06 12:05:57 -07001661 bucket = rcu_dereference(from->rt6i_exception_bucket);
1662
1663#ifdef CONFIG_IPV6_SUBTREES
1664 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1665 * and exception table is indexed by a hash of
1666 * both rt6i_dst and rt6i_src.
1667 * Otherwise, the exception table is indexed by
1668 * a hash of only rt6i_dst.
1669 */
David Ahern93c2fb22018-04-18 15:38:59 -07001670 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001671 src_key = &rt->rt6i_src.addr;
1672#endif
1673 rt6_ex = __rt6_find_exception_rcu(&bucket,
1674 &rt->rt6i_dst.addr,
1675 src_key);
1676 if (rt6_ex)
1677 rt6_ex->stamp = jiffies;
1678
Paolo Abeni193f3682019-02-21 11:19:41 +01001679unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001680 rcu_read_unlock();
1681}
1682
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001683static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1684 struct rt6_info *rt, int mtu)
1685{
1686 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1687 * lowest MTU in the path: always allow updating the route PMTU to
1688 * reflect PMTU decreases.
1689 *
1690 * If the new MTU is higher, and the route PMTU is equal to the local
1691 * MTU, this means the old MTU is the lowest in the path, so allow
1692 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1693 * handle this.
1694 */
1695
1696 if (dst_mtu(&rt->dst) >= mtu)
1697 return true;
1698
1699 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1700 return true;
1701
1702 return false;
1703}
1704
1705static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001706 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001707{
1708 struct rt6_exception_bucket *bucket;
1709 struct rt6_exception *rt6_ex;
1710 int i;
1711
1712 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1713 lockdep_is_held(&rt6_exception_lock));
1714
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001715 if (!bucket)
1716 return;
1717
1718 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1719 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1720 struct rt6_info *entry = rt6_ex->rt6i;
1721
1722 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001723 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001724 * been updated.
1725 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001726 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001727 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001728 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001729 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001730 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001731 }
1732}
1733
Wei Wangb16cb452017-10-06 12:06:00 -07001734#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1735
David Ahern8d1c8022018-04-17 17:33:26 -07001736static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001737 struct in6_addr *gateway)
1738{
1739 struct rt6_exception_bucket *bucket;
1740 struct rt6_exception *rt6_ex;
1741 struct hlist_node *tmp;
1742 int i;
1743
1744 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1745 return;
1746
1747 spin_lock_bh(&rt6_exception_lock);
1748 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1749 lockdep_is_held(&rt6_exception_lock));
1750
1751 if (bucket) {
1752 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1753 hlist_for_each_entry_safe(rt6_ex, tmp,
1754 &bucket->chain, hlist) {
1755 struct rt6_info *entry = rt6_ex->rt6i;
1756
1757 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1758 RTF_CACHE_GATEWAY &&
1759 ipv6_addr_equal(gateway,
1760 &entry->rt6i_gateway)) {
1761 rt6_remove_exception(bucket, rt6_ex);
1762 }
1763 }
1764 bucket++;
1765 }
1766 }
1767
1768 spin_unlock_bh(&rt6_exception_lock);
1769}
1770
Wei Wangc757faa2017-10-06 12:06:01 -07001771static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1772 struct rt6_exception *rt6_ex,
1773 struct fib6_gc_args *gc_args,
1774 unsigned long now)
1775{
1776 struct rt6_info *rt = rt6_ex->rt6i;
1777
Paolo Abeni1859bac2017-10-19 16:07:11 +02001778 /* we are pruning and obsoleting aged-out and non gateway exceptions
1779 * even if others have still references to them, so that on next
1780 * dst_check() such references can be dropped.
1781 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1782 * expired, independently from their aging, as per RFC 8201 section 4
1783 */
Wei Wang31afeb42018-01-26 11:40:17 -08001784 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1785 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1786 RT6_TRACE("aging clone %p\n", rt);
1787 rt6_remove_exception(bucket, rt6_ex);
1788 return;
1789 }
1790 } else if (time_after(jiffies, rt->dst.expires)) {
1791 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001792 rt6_remove_exception(bucket, rt6_ex);
1793 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001794 }
1795
1796 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001797 struct neighbour *neigh;
1798 __u8 neigh_flags = 0;
1799
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001800 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1801 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001802 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001803
Wei Wangc757faa2017-10-06 12:06:01 -07001804 if (!(neigh_flags & NTF_ROUTER)) {
1805 RT6_TRACE("purging route %p via non-router but gateway\n",
1806 rt);
1807 rt6_remove_exception(bucket, rt6_ex);
1808 return;
1809 }
1810 }
Wei Wang31afeb42018-01-26 11:40:17 -08001811
Wei Wangc757faa2017-10-06 12:06:01 -07001812 gc_args->more++;
1813}
1814
David Ahern8d1c8022018-04-17 17:33:26 -07001815void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001816 struct fib6_gc_args *gc_args,
1817 unsigned long now)
1818{
1819 struct rt6_exception_bucket *bucket;
1820 struct rt6_exception *rt6_ex;
1821 struct hlist_node *tmp;
1822 int i;
1823
1824 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1825 return;
1826
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001827 rcu_read_lock_bh();
1828 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001829 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1830 lockdep_is_held(&rt6_exception_lock));
1831
1832 if (bucket) {
1833 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1834 hlist_for_each_entry_safe(rt6_ex, tmp,
1835 &bucket->chain, hlist) {
1836 rt6_age_examine_exception(bucket, rt6_ex,
1837 gc_args, now);
1838 }
1839 bucket++;
1840 }
1841 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001842 spin_unlock(&rt6_exception_lock);
1843 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001844}
1845
David Ahern1d053da2018-05-09 20:34:21 -07001846/* must be called with rcu lock held */
David Aherneffda4d2019-04-16 14:36:10 -07001847int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
1848 struct flowi6 *fl6, struct fib6_result *res, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001850 struct fib6_node *fn, *saved_fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851
David Ahern64547432018-05-09 20:34:19 -07001852 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001853 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854
David Ahernca254492015-10-12 11:47:10 -07001855 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1856 oif = 0;
1857
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001858redo_rt6_select:
David Aherneffda4d2019-04-16 14:36:10 -07001859 rt6_select(net, fn, oif, res, strict);
1860 if (res->f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001861 fn = fib6_backtrack(fn, &fl6->saddr);
1862 if (fn)
1863 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001864 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1865 /* also consider unreachable route */
1866 strict &= ~RT6_LOOKUP_F_REACHABLE;
1867 fn = saved_fn;
1868 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001869 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001870 }
1871
David Aherneffda4d2019-04-16 14:36:10 -07001872 trace_fib6_table_lookup(net, res, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001873
David Aherneffda4d2019-04-16 14:36:10 -07001874 return 0;
David Ahern1d053da2018-05-09 20:34:21 -07001875}
1876
1877struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1878 int oif, struct flowi6 *fl6,
1879 const struct sk_buff *skb, int flags)
1880{
David Ahernb1d40992019-04-16 14:35:59 -07001881 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001882 struct rt6_info *rt;
1883 int strict = 0;
1884
1885 strict |= flags & RT6_LOOKUP_F_IFACE;
1886 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1887 if (net->ipv6.devconf_all->forwarding == 0)
1888 strict |= RT6_LOOKUP_F_REACHABLE;
1889
1890 rcu_read_lock();
1891
David Aherneffda4d2019-04-16 14:36:10 -07001892 fib6_table_lookup(net, table, oif, fl6, &res, strict);
David Ahernb1d40992019-04-16 14:35:59 -07001893 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001894 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001895 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001896 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001897 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001898 }
1899
David Ahernb1d40992019-04-16 14:35:59 -07001900 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001901
David Ahern23fb93a2018-04-17 17:33:23 -07001902 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001903 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001904 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001905 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001906 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001907
Wei Wang66f5d6c2017-10-06 12:06:10 -07001908 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001909 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001910 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001911 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001912 /* Create a RTF_CACHE clone which will not be
1913 * owned by the fib6 tree. It is for the special case where
1914 * the daddr in the skb during the neighbor look-up is different
1915 * from the fl6->daddr used to look-up route here.
1916 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001917 struct rt6_info *uncached_rt;
1918
David Ahern85bd05d2019-04-16 14:36:01 -07001919 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001920
David Ahern4d85cd02018-04-20 15:37:59 -07001921 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001922
Wei Wang1cfb71e2017-06-17 10:42:33 -07001923 if (uncached_rt) {
1924 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1925 * No need for another dst_hold()
1926 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001927 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001928 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001929 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001930 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001931 dst_hold(&uncached_rt->dst);
1932 }
David Ahernb8115802015-11-19 12:24:22 -08001933
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001934 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001935 } else {
1936 /* Get a percpu copy */
1937
1938 struct rt6_info *pcpu_rt;
1939
Eric Dumazet951f7882017-10-08 21:07:18 -07001940 local_bh_disable();
David Aherndb3fede2019-04-16 14:36:03 -07001941 pcpu_rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001942
David Ahern93531c62018-04-17 17:33:25 -07001943 if (!pcpu_rt)
David Aherndb3fede2019-04-16 14:36:03 -07001944 pcpu_rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07001945
Eric Dumazet951f7882017-10-08 21:07:18 -07001946 local_bh_enable();
1947 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001948
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001949 return pcpu_rt;
1950 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001951}
David Ahern9ff74382016-06-13 13:44:19 -07001952EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001953
David Ahernb75cc8f2018-03-02 08:32:17 -08001954static struct rt6_info *ip6_pol_route_input(struct net *net,
1955 struct fib6_table *table,
1956 struct flowi6 *fl6,
1957 const struct sk_buff *skb,
1958 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001959{
David Ahernb75cc8f2018-03-02 08:32:17 -08001960 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001961}
1962
Mahesh Bandeward409b842016-09-16 12:59:08 -07001963struct dst_entry *ip6_route_input_lookup(struct net *net,
1964 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001965 struct flowi6 *fl6,
1966 const struct sk_buff *skb,
1967 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001968{
1969 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1970 flags |= RT6_LOOKUP_F_IFACE;
1971
David Ahernb75cc8f2018-03-02 08:32:17 -08001972 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001973}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001974EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001975
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001976static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001977 struct flow_keys *keys,
1978 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001979{
1980 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1981 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001982 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001983 const struct ipv6hdr *inner_iph;
1984 const struct icmp6hdr *icmph;
1985 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001986 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001987
1988 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1989 goto out;
1990
Eric Dumazetcea67a22018-04-29 09:54:59 -07001991 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1992 sizeof(_icmph), &_icmph);
1993 if (!icmph)
1994 goto out;
1995
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001996 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1997 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1998 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1999 icmph->icmp6_type != ICMPV6_PARAMPROB)
2000 goto out;
2001
2002 inner_iph = skb_header_pointer(skb,
2003 skb_transport_offset(skb) + sizeof(*icmph),
2004 sizeof(_inner_iph), &_inner_iph);
2005 if (!inner_iph)
2006 goto out;
2007
2008 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002009 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002010out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002011 if (_flkeys) {
2012 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
2013 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
2014 keys->tags.flow_label = _flkeys->tags.flow_label;
2015 keys->basic.ip_proto = _flkeys->basic.ip_proto;
2016 } else {
2017 keys->addrs.v6addrs.src = key_iph->saddr;
2018 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002019 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002020 keys->basic.ip_proto = key_iph->nexthdr;
2021 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002022}
2023
2024/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002025u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2026 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002027{
2028 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002029 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002030
David S. Millerbbfa0472018-03-12 11:09:33 -04002031 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002032 case 0:
2033 memset(&hash_keys, 0, sizeof(hash_keys));
2034 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2035 if (skb) {
2036 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2037 } else {
2038 hash_keys.addrs.v6addrs.src = fl6->saddr;
2039 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002040 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002041 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2042 }
2043 break;
2044 case 1:
2045 if (skb) {
2046 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2047 struct flow_keys keys;
2048
2049 /* short-circuit if we already have L4 hash present */
2050 if (skb->l4_hash)
2051 return skb_get_hash_raw(skb) >> 1;
2052
2053 memset(&hash_keys, 0, sizeof(hash_keys));
2054
2055 if (!flkeys) {
2056 skb_flow_dissect_flow_keys(skb, &keys, flag);
2057 flkeys = &keys;
2058 }
2059 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2060 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2061 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2062 hash_keys.ports.src = flkeys->ports.src;
2063 hash_keys.ports.dst = flkeys->ports.dst;
2064 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2065 } else {
2066 memset(&hash_keys, 0, sizeof(hash_keys));
2067 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2068 hash_keys.addrs.v6addrs.src = fl6->saddr;
2069 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2070 hash_keys.ports.src = fl6->fl6_sport;
2071 hash_keys.ports.dst = fl6->fl6_dport;
2072 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2073 }
2074 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002075 }
David Ahern9a2a5372018-03-02 08:32:15 -08002076 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002077
David Ahern9a2a5372018-03-02 08:32:15 -08002078 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002079}
2080
Thomas Grafc71099a2006-08-04 23:20:06 -07002081void ip6_route_input(struct sk_buff *skb)
2082{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002083 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002084 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002085 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002086 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002087 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002088 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002089 .daddr = iph->daddr,
2090 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002091 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002092 .flowi6_mark = skb->mark,
2093 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002094 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002095 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002096
Jiri Benc904af042015-08-20 13:56:31 +02002097 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002098 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002099 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002100
2101 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2102 flkeys = &_flkeys;
2103
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002104 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002105 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002106 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002107 skb_dst_set(skb,
2108 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002109}
2110
David Ahernb75cc8f2018-03-02 08:32:17 -08002111static struct rt6_info *ip6_pol_route_output(struct net *net,
2112 struct fib6_table *table,
2113 struct flowi6 *fl6,
2114 const struct sk_buff *skb,
2115 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002116{
David Ahernb75cc8f2018-03-02 08:32:17 -08002117 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002118}
2119
Paolo Abeni6f21c962016-01-29 12:30:19 +01002120struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2121 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002122{
David Ahernd46a9d62015-10-21 08:42:22 -07002123 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002124
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002125 if (ipv6_addr_type(&fl6->daddr) &
2126 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002127 struct dst_entry *dst;
2128
2129 dst = l3mdev_link_scope_lookup(net, fl6);
2130 if (dst)
2131 return dst;
2132 }
David Ahernca254492015-10-12 11:47:10 -07002133
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002134 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002135
David Ahernd46a9d62015-10-21 08:42:22 -07002136 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002137 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002138 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002139 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002140
David Ahernd46a9d62015-10-21 08:42:22 -07002141 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002142 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002143 else if (sk)
2144 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002145
David Ahernb75cc8f2018-03-02 08:32:17 -08002146 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002148EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
David S. Miller2774c132011-03-01 14:59:04 -08002150struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002151{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002152 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002153 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002154 struct dst_entry *new = NULL;
2155
Wei Wang1dbe32522017-06-17 10:42:26 -07002156 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002157 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002158 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002159 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002160 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002161
Changli Gaod8d1f302010-06-10 23:31:35 -07002162 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002163 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002164 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002165 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002166
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002167 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002168
Wei Wang1dbe32522017-06-17 10:42:26 -07002169 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002170 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002171 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002172
2173 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2174#ifdef CONFIG_IPV6_SUBTREES
2175 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2176#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002177 }
2178
David S. Miller69ead7a2011-03-01 14:45:33 -08002179 dst_release(dst_orig);
2180 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002181}
David S. Miller14e50e52007-05-24 18:17:54 -07002182
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183/*
2184 * Destination cache support functions
2185 */
2186
David Ahern8d1c8022018-04-17 17:33:26 -07002187static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002188{
Steffen Klassert36143642017-08-25 09:05:42 +02002189 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002190
David Ahern8ae86972018-04-20 15:38:03 -07002191 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002192 return false;
2193
2194 if (fib6_check_expired(f6i))
2195 return false;
2196
2197 return true;
2198}
2199
David Aherna68886a2018-04-20 15:38:02 -07002200static struct dst_entry *rt6_check(struct rt6_info *rt,
2201 struct fib6_info *from,
2202 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002203{
Wei Wangc5cff852017-08-21 09:47:10 -07002204 u32 rt_cookie = 0;
2205
David Aherna68886a2018-04-20 15:38:02 -07002206 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002207 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002208 return NULL;
2209
2210 if (rt6_check_expired(rt))
2211 return NULL;
2212
2213 return &rt->dst;
2214}
2215
David Aherna68886a2018-04-20 15:38:02 -07002216static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2217 struct fib6_info *from,
2218 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002219{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002220 if (!__rt6_check_expired(rt) &&
2221 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002222 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002223 return &rt->dst;
2224 else
2225 return NULL;
2226}
2227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2229{
David Aherna87b7dc2018-04-20 15:38:00 -07002230 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002231 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 struct rt6_info *rt;
2233
David Aherna87b7dc2018-04-20 15:38:00 -07002234 rt = container_of(dst, struct rt6_info, dst);
2235
2236 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002238 /* All IPV6 dsts are created with ->obsolete set to the value
2239 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2240 * into this function always.
2241 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002242
David Aherna68886a2018-04-20 15:38:02 -07002243 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002244
David Aherna68886a2018-04-20 15:38:02 -07002245 if (from && (rt->rt6i_flags & RTF_PCPU ||
2246 unlikely(!list_empty(&rt->rt6i_uncached))))
2247 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002248 else
David Aherna68886a2018-04-20 15:38:02 -07002249 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002250
2251 rcu_read_unlock();
2252
2253 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254}
2255
2256static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2257{
2258 struct rt6_info *rt = (struct rt6_info *) dst;
2259
2260 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002261 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002262 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002263 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002264 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002265 dst = NULL;
2266 }
David Ahernc3c14da2018-04-23 11:32:06 -07002267 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002268 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002270 dst = NULL;
2271 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002273 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274}
2275
2276static void ip6_link_failure(struct sk_buff *skb)
2277{
2278 struct rt6_info *rt;
2279
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002280 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281
Eric Dumazetadf30902009-06-02 05:19:30 +00002282 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002284 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002285 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002286 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002287 } else {
David Aherna68886a2018-04-20 15:38:02 -07002288 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002289 struct fib6_node *fn;
2290
David Aherna68886a2018-04-20 15:38:02 -07002291 from = rcu_dereference(rt->from);
2292 if (from) {
2293 fn = rcu_dereference(from->fib6_node);
2294 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2295 fn->fn_sernum = -1;
2296 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002297 }
David Ahern8a14e462018-04-23 11:32:07 -07002298 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 }
2300}
2301
David Ahern6a3e0302018-04-20 15:37:57 -07002302static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2303{
David Aherna68886a2018-04-20 15:38:02 -07002304 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2305 struct fib6_info *from;
2306
2307 rcu_read_lock();
2308 from = rcu_dereference(rt0->from);
2309 if (from)
2310 rt0->dst.expires = from->expires;
2311 rcu_read_unlock();
2312 }
David Ahern6a3e0302018-04-20 15:37:57 -07002313
2314 dst_set_expires(&rt0->dst, timeout);
2315 rt0->rt6i_flags |= RTF_EXPIRES;
2316}
2317
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002318static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2319{
2320 struct net *net = dev_net(rt->dst.dev);
2321
David Ahernd4ead6b2018-04-17 17:33:16 -07002322 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002323 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002324 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2325}
2326
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002327static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2328{
2329 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002330 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002331}
2332
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002333static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2334 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002336 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002337 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
Xin Long19bda362016-10-28 18:18:01 +08002339 if (dst_metric_locked(dst, RTAX_MTU))
2340 return;
2341
Julian Anastasov0dec8792017-02-06 23:14:16 +02002342 if (iph) {
2343 daddr = &iph->daddr;
2344 saddr = &iph->saddr;
2345 } else if (sk) {
2346 daddr = &sk->sk_v6_daddr;
2347 saddr = &inet6_sk(sk)->saddr;
2348 } else {
2349 daddr = NULL;
2350 saddr = NULL;
2351 }
2352 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002353 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2354 if (mtu >= dst_mtu(dst))
2355 return;
David S. Miller81aded22012-06-15 14:54:11 -07002356
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002357 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002358 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002359 /* update rt6_ex->stamp for cache */
2360 if (rt6->rt6i_flags & RTF_CACHE)
2361 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002362 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002363 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002364 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002365
David Ahern4d85cd02018-04-20 15:37:59 -07002366 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002367 res.f6i = rcu_dereference(rt6->from);
2368 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002369 rcu_read_unlock();
2370 return;
2371 }
David Ahern85bd05d2019-04-16 14:36:01 -07002372 res.nh = &res.f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -07002373 res.fib6_flags = res.f6i->fib6_flags;
2374 res.fib6_type = res.f6i->fib6_type;
2375
David Ahern85bd05d2019-04-16 14:36:01 -07002376 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002377 if (nrt6) {
2378 rt6_do_update_pmtu(nrt6, mtu);
David Ahern5012f0a2019-04-16 14:36:05 -07002379 if (rt6_insert_exception(nrt6, &res))
Wei Wang2b760fc2017-10-06 12:06:03 -07002380 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002381 }
David Aherna68886a2018-04-20 15:38:02 -07002382 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 }
2384}
2385
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002386static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2387 struct sk_buff *skb, u32 mtu)
2388{
2389 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2390}
2391
David S. Miller42ae66c2012-06-15 20:01:57 -07002392void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002393 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002394{
2395 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2396 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002397 struct flowi6 fl6 = {
2398 .flowi6_oif = oif,
2399 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2400 .daddr = iph->daddr,
2401 .saddr = iph->saddr,
2402 .flowlabel = ip6_flowinfo(iph),
2403 .flowi6_uid = uid,
2404 };
David S. Miller81aded22012-06-15 14:54:11 -07002405
2406 dst = ip6_route_output(net, NULL, &fl6);
2407 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002408 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002409 dst_release(dst);
2410}
2411EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2412
2413void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2414{
David Ahern7ddacfa2018-11-18 10:45:30 -08002415 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002416 struct dst_entry *dst;
2417
David Ahern7ddacfa2018-11-18 10:45:30 -08002418 if (!oif && skb->dev)
2419 oif = l3mdev_master_ifindex(skb->dev);
2420
2421 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002422
2423 dst = __sk_dst_get(sk);
2424 if (!dst || !dst->obsolete ||
2425 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2426 return;
2427
2428 bh_lock_sock(sk);
2429 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2430 ip6_datagram_dst_update(sk, false);
2431 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002432}
2433EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2434
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002435void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2436 const struct flowi6 *fl6)
2437{
2438#ifdef CONFIG_IPV6_SUBTREES
2439 struct ipv6_pinfo *np = inet6_sk(sk);
2440#endif
2441
2442 ip6_dst_store(sk, dst,
2443 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2444 &sk->sk_v6_daddr : NULL,
2445#ifdef CONFIG_IPV6_SUBTREES
2446 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2447 &np->saddr :
2448#endif
2449 NULL);
2450}
2451
David Ahern9b6b35a2019-04-16 14:36:02 -07002452static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002453 struct flowi6 *fl6,
2454 const struct in6_addr *gw,
2455 struct rt6_info **ret)
2456{
David Ahern9b6b35a2019-04-16 14:36:02 -07002457 const struct fib6_nh *nh = res->nh;
2458
David Ahern0b34eb02019-04-09 14:41:19 -07002459 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2460 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2461 return false;
2462
2463 /* rt_cache's gateway might be different from its 'parent'
2464 * in the case of an ip redirect.
2465 * So we keep searching in the exception table if the gateway
2466 * is different.
2467 */
2468 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2469 struct rt6_info *rt_cache;
2470
David Ahern9b6b35a2019-04-16 14:36:02 -07002471 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002472 if (rt_cache &&
2473 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2474 *ret = rt_cache;
2475 return true;
2476 }
2477 return false;
2478 }
2479 return true;
2480}
2481
Duan Jiongb55b76b2013-09-04 19:44:21 +08002482/* Handle redirects */
2483struct ip6rd_flowi {
2484 struct flowi6 fl6;
2485 struct in6_addr gateway;
2486};
2487
2488static struct rt6_info *__ip6_route_redirect(struct net *net,
2489 struct fib6_table *table,
2490 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002491 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002492 int flags)
2493{
2494 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002495 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002496 struct fib6_result res = {};
David Ahern8d1c8022018-04-17 17:33:26 -07002497 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002498 struct fib6_node *fn;
2499
2500 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002501 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002502 *
2503 * RFC 4861 specifies that redirects should only be
2504 * accepted if they come from the nexthop to the target.
2505 * Due to the way the routes are chosen, this notion
2506 * is a bit fuzzy and one might need to check all possible
2507 * routes.
2508 */
2509
Wei Wang66f5d6c2017-10-06 12:06:10 -07002510 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002511 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002512restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002513 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002514 res.f6i = rt;
2515 res.nh = &rt->fib6_nh;
2516
David Ahern14895682018-04-17 17:33:17 -07002517 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002518 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002519 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002520 break;
David Ahern9b6b35a2019-04-16 14:36:02 -07002521 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
David Ahern0b34eb02019-04-09 14:41:19 -07002522 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002523 }
2524
2525 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002526 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002527 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002528 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002529 goto out;
2530 }
2531
David Ahern421842e2018-04-17 17:33:18 -07002532 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002533 fn = fib6_backtrack(fn, &fl6->saddr);
2534 if (fn)
2535 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002536 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002537
David Ahern9b6b35a2019-04-16 14:36:02 -07002538 res.f6i = rt;
2539 res.nh = &rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002540out:
David Ahern7d21fec2019-04-16 14:36:11 -07002541 if (ret) {
David Ahern10585b42019-03-20 09:24:50 -07002542 ip6_hold_safe(net, &ret);
David Ahern7d21fec2019-04-16 14:36:11 -07002543 } else {
2544 res.fib6_flags = res.f6i->fib6_flags;
2545 res.fib6_type = res.f6i->fib6_type;
David Ahern9b6b35a2019-04-16 14:36:02 -07002546 ret = ip6_create_rt_rcu(&res);
David Ahern7d21fec2019-04-16 14:36:11 -07002547 }
Duan Jiongb55b76b2013-09-04 19:44:21 +08002548
Wei Wang66f5d6c2017-10-06 12:06:10 -07002549 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002550
David Ahern8ff2e5b2019-04-16 14:36:09 -07002551 trace_fib6_table_lookup(net, &res, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002552 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002553};
2554
2555static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002556 const struct flowi6 *fl6,
2557 const struct sk_buff *skb,
2558 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002559{
2560 int flags = RT6_LOOKUP_F_HAS_SADDR;
2561 struct ip6rd_flowi rdfl;
2562
2563 rdfl.fl6 = *fl6;
2564 rdfl.gateway = *gateway;
2565
David Ahernb75cc8f2018-03-02 08:32:17 -08002566 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002567 flags, __ip6_route_redirect);
2568}
2569
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002570void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2571 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002572{
2573 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2574 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002575 struct flowi6 fl6 = {
2576 .flowi6_iif = LOOPBACK_IFINDEX,
2577 .flowi6_oif = oif,
2578 .flowi6_mark = mark,
2579 .daddr = iph->daddr,
2580 .saddr = iph->saddr,
2581 .flowlabel = ip6_flowinfo(iph),
2582 .flowi6_uid = uid,
2583 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002584
David Ahernb75cc8f2018-03-02 08:32:17 -08002585 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002586 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002587 dst_release(dst);
2588}
2589EXPORT_SYMBOL_GPL(ip6_redirect);
2590
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002591void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002592{
2593 const struct ipv6hdr *iph = ipv6_hdr(skb);
2594 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2595 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002596 struct flowi6 fl6 = {
2597 .flowi6_iif = LOOPBACK_IFINDEX,
2598 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002599 .daddr = msg->dest,
2600 .saddr = iph->daddr,
2601 .flowi6_uid = sock_net_uid(net, NULL),
2602 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002603
David Ahernb75cc8f2018-03-02 08:32:17 -08002604 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002605 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002606 dst_release(dst);
2607}
2608
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002609void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2610{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002611 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2612 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002613}
2614EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2615
David S. Miller0dbaee32010-12-13 12:52:14 -08002616static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617{
David S. Miller0dbaee32010-12-13 12:52:14 -08002618 struct net_device *dev = dst->dev;
2619 unsigned int mtu = dst_mtu(dst);
2620 struct net *net = dev_net(dev);
2621
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2623
Daniel Lezcano55786892008-03-04 13:47:47 -08002624 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2625 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626
2627 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002628 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2629 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2630 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 * rely only on pmtu discovery"
2632 */
2633 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2634 mtu = IPV6_MAXPLEN;
2635 return mtu;
2636}
2637
Steffen Klassertebb762f2011-11-23 02:12:51 +00002638static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002639{
David S. Millerd33e4552010-12-14 13:01:14 -08002640 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002641 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002642
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002643 mtu = dst_metric_raw(dst, RTAX_MTU);
2644 if (mtu)
2645 goto out;
2646
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002647 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002648
2649 rcu_read_lock();
2650 idev = __in6_dev_get(dst->dev);
2651 if (idev)
2652 mtu = idev->cnf.mtu6;
2653 rcu_read_unlock();
2654
Eric Dumazet30f78d82014-04-10 21:23:36 -07002655out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002656 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2657
2658 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002659}
2660
David Ahern901731b2018-05-21 09:08:14 -07002661/* MTU selection:
2662 * 1. mtu on route is locked - use it
2663 * 2. mtu from nexthop exception
2664 * 3. mtu from egress device
2665 *
2666 * based on ip6_dst_mtu_forward and exception logic of
2667 * rt6_find_cached_rt; called with rcu_read_lock
2668 */
David Ahernb748f262019-04-16 14:36:06 -07002669u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2670 const struct in6_addr *daddr,
2671 const struct in6_addr *saddr)
David Ahern901731b2018-05-21 09:08:14 -07002672{
2673 struct rt6_exception_bucket *bucket;
David Ahernb748f262019-04-16 14:36:06 -07002674 const struct fib6_nh *nh = res->nh;
2675 struct fib6_info *f6i = res->f6i;
2676 const struct in6_addr *src_key;
David Ahern901731b2018-05-21 09:08:14 -07002677 struct rt6_exception *rt6_ex;
David Ahern901731b2018-05-21 09:08:14 -07002678 struct inet6_dev *idev;
2679 u32 mtu = 0;
2680
2681 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2682 mtu = f6i->fib6_pmtu;
2683 if (mtu)
2684 goto out;
2685 }
2686
2687 src_key = NULL;
2688#ifdef CONFIG_IPV6_SUBTREES
2689 if (f6i->fib6_src.plen)
2690 src_key = saddr;
2691#endif
2692
2693 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2694 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2695 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2696 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2697
2698 if (likely(!mtu)) {
David Ahernb748f262019-04-16 14:36:06 -07002699 struct net_device *dev = nh->fib_nh_dev;
David Ahern901731b2018-05-21 09:08:14 -07002700
2701 mtu = IPV6_MIN_MTU;
2702 idev = __in6_dev_get(dev);
2703 if (idev && idev->cnf.mtu6 > mtu)
2704 mtu = idev->cnf.mtu6;
2705 }
2706
2707 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2708out:
David Ahernb748f262019-04-16 14:36:06 -07002709 return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu);
David Ahern901731b2018-05-21 09:08:14 -07002710}
2711
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002712struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002713 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714{
David S. Miller87a11572011-12-06 17:04:13 -05002715 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 struct rt6_info *rt;
2717 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002718 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719
David S. Miller38308472011-12-03 18:02:47 -05002720 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002721 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722
Martin KaFai Lauad706862015-08-14 11:05:52 -07002723 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002724 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002726 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 goto out;
2728 }
2729
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002730 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002731 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002732 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002733 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002734 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002735 rt->rt6i_dst.plen = 128;
2736 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002737 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738
Ido Schimmel4c981e22018-01-07 12:45:04 +02002739 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002740 * do proper release of the net_device
2741 */
2742 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002743 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744
David S. Miller87a11572011-12-06 17:04:13 -05002745 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2746
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747out:
David S. Miller87a11572011-12-06 17:04:13 -05002748 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749}
2750
Daniel Lezcano569d3642008-01-18 03:56:57 -08002751static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002753 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002754 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2755 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2756 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2757 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2758 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002759 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760
Eric Dumazetfc66f952010-10-08 06:37:34 +00002761 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002762 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002763 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 goto out;
2765
Benjamin Thery6891a342008-03-04 13:49:47 -08002766 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002767 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002768 entries = dst_entries_get_slow(ops);
2769 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002770 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002772 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002773 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774}
2775
David Ahern8c145862016-04-24 21:26:04 -07002776static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2777 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002778 const struct in6_addr *gw_addr,
2779 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002780{
2781 struct flowi6 fl6 = {
2782 .flowi6_oif = cfg->fc_ifindex,
2783 .daddr = *gw_addr,
2784 .saddr = cfg->fc_prefsrc,
2785 };
2786 struct fib6_table *table;
2787 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002788
David Ahernf4797b32018-01-25 16:55:08 -08002789 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002790 if (!table)
2791 return NULL;
2792
2793 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2794 flags |= RT6_LOOKUP_F_HAS_SADDR;
2795
David Ahernf4797b32018-01-25 16:55:08 -08002796 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002797 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002798
2799 /* if table lookup failed, fall back to full lookup */
2800 if (rt == net->ipv6.ip6_null_entry) {
2801 ip6_rt_put(rt);
2802 rt = NULL;
2803 }
2804
2805 return rt;
2806}
2807
David Ahernfc1e64e2018-01-25 16:55:09 -08002808static int ip6_route_check_nh_onlink(struct net *net,
2809 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002810 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002811 struct netlink_ext_ack *extack)
2812{
David Ahern44750f82018-02-06 13:17:06 -08002813 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002814 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2815 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002816 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002817 struct rt6_info *grt;
2818 int err;
2819
2820 err = 0;
2821 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2822 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002823 rcu_read_lock();
2824 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002825 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002826 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002827 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002828 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002829 NL_SET_ERR_MSG(extack,
2830 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002831 err = -EINVAL;
2832 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002833 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002834
2835 ip6_rt_put(grt);
2836 }
2837
2838 return err;
2839}
2840
David Ahern1edce992018-01-25 16:55:07 -08002841static int ip6_route_check_nh(struct net *net,
2842 struct fib6_config *cfg,
2843 struct net_device **_dev,
2844 struct inet6_dev **idev)
2845{
2846 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2847 struct net_device *dev = _dev ? *_dev : NULL;
2848 struct rt6_info *grt = NULL;
2849 int err = -EHOSTUNREACH;
2850
2851 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002852 int flags = RT6_LOOKUP_F_IFACE;
2853
2854 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2855 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002856 if (grt) {
2857 if (grt->rt6i_flags & RTF_GATEWAY ||
2858 (dev && dev != grt->dst.dev)) {
2859 ip6_rt_put(grt);
2860 grt = NULL;
2861 }
2862 }
2863 }
2864
2865 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002866 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002867
2868 if (!grt)
2869 goto out;
2870
2871 if (dev) {
2872 if (dev != grt->dst.dev) {
2873 ip6_rt_put(grt);
2874 goto out;
2875 }
2876 } else {
2877 *_dev = dev = grt->dst.dev;
2878 *idev = grt->rt6i_idev;
2879 dev_hold(dev);
2880 in6_dev_hold(grt->rt6i_idev);
2881 }
2882
2883 if (!(grt->rt6i_flags & RTF_GATEWAY))
2884 err = 0;
2885
2886 ip6_rt_put(grt);
2887
2888out:
2889 return err;
2890}
2891
David Ahern9fbb7042018-03-13 08:29:36 -07002892static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2893 struct net_device **_dev, struct inet6_dev **idev,
2894 struct netlink_ext_ack *extack)
2895{
2896 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2897 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002898 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002899 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002900 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002901 int err = -EINVAL;
2902
2903 /* if gw_addr is local we will fail to detect this in case
2904 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2905 * will return already-added prefix route via interface that
2906 * prefix route was assigned to, which might be non-loopback.
2907 */
David Ahern232378e2018-03-13 08:29:37 -07002908 if (dev &&
2909 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2910 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002911 goto out;
2912 }
2913
2914 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2915 /* IPv6 strictly inhibits using not link-local
2916 * addresses as nexthop address.
2917 * Otherwise, router will not able to send redirects.
2918 * It is very good, but in some (rare!) circumstances
2919 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2920 * some exceptions. --ANK
2921 * We allow IPv4-mapped nexthops to support RFC4798-type
2922 * addressing
2923 */
2924 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2925 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2926 goto out;
2927 }
2928
2929 if (cfg->fc_flags & RTNH_F_ONLINK)
2930 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2931 else
2932 err = ip6_route_check_nh(net, cfg, _dev, idev);
2933
2934 if (err)
2935 goto out;
2936 }
2937
2938 /* reload in case device was changed */
2939 dev = *_dev;
2940
2941 err = -EINVAL;
2942 if (!dev) {
2943 NL_SET_ERR_MSG(extack, "Egress device not specified");
2944 goto out;
2945 } else if (dev->flags & IFF_LOOPBACK) {
2946 NL_SET_ERR_MSG(extack,
2947 "Egress device can not be loopback device for this route");
2948 goto out;
2949 }
David Ahern232378e2018-03-13 08:29:37 -07002950
2951 /* if we did not check gw_addr above, do so now that the
2952 * egress device has been resolved.
2953 */
2954 if (need_addr_check &&
2955 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2956 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2957 goto out;
2958 }
2959
David Ahern9fbb7042018-03-13 08:29:36 -07002960 err = 0;
2961out:
2962 return err;
2963}
2964
David Ahern83c442512019-03-27 20:53:50 -07002965static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2966{
2967 if ((flags & RTF_REJECT) ||
2968 (dev && (dev->flags & IFF_LOOPBACK) &&
2969 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2970 !(flags & RTF_LOCAL)))
2971 return true;
2972
2973 return false;
2974}
2975
2976int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2977 struct fib6_config *cfg, gfp_t gfp_flags,
2978 struct netlink_ext_ack *extack)
2979{
2980 struct net_device *dev = NULL;
2981 struct inet6_dev *idev = NULL;
2982 int addr_type;
2983 int err;
2984
David Ahernf1741732019-03-27 20:53:57 -07002985 fib6_nh->fib_nh_family = AF_INET6;
2986
David Ahern83c442512019-03-27 20:53:50 -07002987 err = -ENODEV;
2988 if (cfg->fc_ifindex) {
2989 dev = dev_get_by_index(net, cfg->fc_ifindex);
2990 if (!dev)
2991 goto out;
2992 idev = in6_dev_get(dev);
2993 if (!idev)
2994 goto out;
2995 }
2996
2997 if (cfg->fc_flags & RTNH_F_ONLINK) {
2998 if (!dev) {
2999 NL_SET_ERR_MSG(extack,
3000 "Nexthop device required for onlink");
3001 goto out;
3002 }
3003
3004 if (!(dev->flags & IFF_UP)) {
3005 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3006 err = -ENETDOWN;
3007 goto out;
3008 }
3009
David Ahernad1601a2019-03-27 20:53:56 -07003010 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07003011 }
3012
David Ahernad1601a2019-03-27 20:53:56 -07003013 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07003014
3015 /* We cannot add true routes via loopback here,
3016 * they would result in kernel looping; promote them to reject routes
3017 */
3018 addr_type = ipv6_addr_type(&cfg->fc_dst);
3019 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
3020 /* hold loopback dev/idev if we haven't done so. */
3021 if (dev != net->loopback_dev) {
3022 if (dev) {
3023 dev_put(dev);
3024 in6_dev_put(idev);
3025 }
3026 dev = net->loopback_dev;
3027 dev_hold(dev);
3028 idev = in6_dev_get(dev);
3029 if (!idev) {
3030 err = -ENODEV;
3031 goto out;
3032 }
3033 }
3034 goto set_dev;
3035 }
3036
3037 if (cfg->fc_flags & RTF_GATEWAY) {
3038 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3039 if (err)
3040 goto out;
3041
David Ahernad1601a2019-03-27 20:53:56 -07003042 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003043 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003044 }
3045
3046 err = -ENODEV;
3047 if (!dev)
3048 goto out;
3049
3050 if (idev->cnf.disable_ipv6) {
3051 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3052 err = -EACCES;
3053 goto out;
3054 }
3055
3056 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3057 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3058 err = -ENETDOWN;
3059 goto out;
3060 }
3061
3062 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3063 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003064 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003065
David Ahern979e2762019-03-27 20:53:58 -07003066 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3067 cfg->fc_encap_type, cfg, gfp_flags, extack);
3068 if (err)
3069 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003070set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003071 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003072 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003073 err = 0;
3074out:
3075 if (idev)
3076 in6_dev_put(idev);
3077
3078 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003079 lwtstate_put(fib6_nh->fib_nh_lws);
3080 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003081 if (dev)
3082 dev_put(dev);
3083 }
3084
3085 return err;
3086}
3087
David Aherndac7d0f2019-03-27 20:53:51 -07003088void fib6_nh_release(struct fib6_nh *fib6_nh)
3089{
David Ahern979e2762019-03-27 20:53:58 -07003090 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003091}
3092
David Ahern8d1c8022018-04-17 17:33:26 -07003093static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003094 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003095 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003096{
Daniel Lezcano55786892008-03-04 13:47:47 -08003097 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003098 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003099 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003100 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003101 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003102
David Ahern557c44b2017-04-19 14:19:43 -07003103 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003104 if (cfg->fc_flags & RTF_PCPU) {
3105 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003106 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003107 }
David Ahern557c44b2017-04-19 14:19:43 -07003108
Wei Wang2ea23522017-10-27 17:30:12 -07003109 /* RTF_CACHE is an internal flag; can not be set by userspace */
3110 if (cfg->fc_flags & RTF_CACHE) {
3111 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3112 goto out;
3113 }
3114
David Aherne8478e82018-04-17 17:33:13 -07003115 if (cfg->fc_type > RTN_MAX) {
3116 NL_SET_ERR_MSG(extack, "Invalid route type");
3117 goto out;
3118 }
3119
David Ahernd5d531c2017-05-21 10:12:05 -06003120 if (cfg->fc_dst_len > 128) {
3121 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003122 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003123 }
3124 if (cfg->fc_src_len > 128) {
3125 NL_SET_ERR_MSG(extack, "Invalid source address length");
3126 goto out;
3127 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003128#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003129 if (cfg->fc_src_len) {
3130 NL_SET_ERR_MSG(extack,
3131 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003132 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003133 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003135
Matti Vaittinend71314b2011-11-14 00:14:49 +00003136 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003137 if (cfg->fc_nlinfo.nlh &&
3138 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003139 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003140 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003141 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003142 table = fib6_new_table(net, cfg->fc_table);
3143 }
3144 } else {
3145 table = fib6_new_table(net, cfg->fc_table);
3146 }
David S. Miller38308472011-12-03 18:02:47 -05003147
3148 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003149 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003150
David Ahern93531c62018-04-17 17:33:25 -07003151 err = -ENOMEM;
3152 rt = fib6_info_alloc(gfp_flags);
3153 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003155
David Ahernd7e774f2018-11-06 12:51:15 -08003156 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3157 extack);
David Ahern767a2212018-10-04 20:07:51 -07003158 if (IS_ERR(rt->fib6_metrics)) {
3159 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003160 /* Do not leave garbage there. */
3161 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003162 goto out;
3163 }
3164
David Ahern93531c62018-04-17 17:33:25 -07003165 if (cfg->fc_flags & RTF_ADDRCONF)
3166 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003167
Gao feng1716a962012-04-06 00:13:10 +00003168 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003169 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003170 clock_t_to_jiffies(cfg->fc_expires));
3171 else
David Ahern14895682018-04-17 17:33:17 -07003172 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173
Thomas Graf86872cb2006-08-22 00:01:08 -07003174 if (cfg->fc_protocol == RTPROT_UNSPEC)
3175 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003176 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003177
David Ahern83c442512019-03-27 20:53:50 -07003178 rt->fib6_table = table;
3179 rt->fib6_metric = cfg->fc_metric;
3180 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003181 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003182
David Ahern93c2fb22018-04-18 15:38:59 -07003183 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3184 rt->fib6_dst.plen = cfg->fc_dst_len;
3185 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003186 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003187
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003189 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3190 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191#endif
David Ahern83c442512019-03-27 20:53:50 -07003192 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3193 if (err)
3194 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195
3196 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003197 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198 */
David Ahern83c442512019-03-27 20:53:50 -07003199 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003200 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003201 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003202
Daniel Walterc3968a82011-04-13 21:10:57 +00003203 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003204 struct net_device *dev = fib6_info_nh_dev(rt);
3205
Daniel Walterc3968a82011-04-13 21:10:57 +00003206 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003207 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003208 err = -EINVAL;
3209 goto out;
3210 }
David Ahern93c2fb22018-04-18 15:38:59 -07003211 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3212 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003213 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003214 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003215
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003216 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217out:
David Ahern93531c62018-04-17 17:33:25 -07003218 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003219 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003220}
3221
David Ahernacb54e32018-04-17 17:33:22 -07003222int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003223 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003224{
David Ahern8d1c8022018-04-17 17:33:26 -07003225 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003226 int err;
3227
David Ahernacb54e32018-04-17 17:33:22 -07003228 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003229 if (IS_ERR(rt))
3230 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003231
David Ahernd4ead6b2018-04-17 17:33:16 -07003232 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003233 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003234
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235 return err;
3236}
3237
David Ahern8d1c8022018-04-17 17:33:26 -07003238static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239{
David Ahernafb1d4b52018-04-17 17:33:11 -07003240 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003241 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003242 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243
David Ahern421842e2018-04-17 17:33:18 -07003244 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003245 err = -ENOENT;
3246 goto out;
3247 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003248
David Ahern93c2fb22018-04-18 15:38:59 -07003249 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003250 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003251 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003252 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003253
Gao feng6825a262012-09-19 19:25:34 +00003254out:
David Ahern93531c62018-04-17 17:33:25 -07003255 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003256 return err;
3257}
3258
David Ahern8d1c8022018-04-17 17:33:26 -07003259int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003260{
David Ahernafb1d4b52018-04-17 17:33:11 -07003261 struct nl_info info = { .nl_net = net };
3262
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003263 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003264}
3265
David Ahern8d1c8022018-04-17 17:33:26 -07003266static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003267{
3268 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003269 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003270 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003271 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003272 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003273
David Ahern421842e2018-04-17 17:33:18 -07003274 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003275 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003276 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003277 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003278
David Ahern93c2fb22018-04-18 15:38:59 -07003279 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003280 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003281
David Ahern16a16cd2017-02-02 12:37:11 -08003282 /* prefer to send a single notification with all hops */
3283 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3284 if (skb) {
3285 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3286
David Ahernd4ead6b2018-04-17 17:33:16 -07003287 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003288 NULL, NULL, 0, RTM_DELROUTE,
3289 info->portid, seq, 0) < 0) {
3290 kfree_skb(skb);
3291 skb = NULL;
3292 } else
3293 info->skip_notify = 1;
3294 }
3295
David Ahern0ae81332017-02-02 12:37:08 -08003296 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003297 &rt->fib6_siblings,
3298 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003299 err = fib6_del(sibling, info);
3300 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003301 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003302 }
3303 }
3304
3305 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003306out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003307 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003308out_put:
David Ahern93531c62018-04-17 17:33:25 -07003309 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003310
3311 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003312 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003313 info->nlh, gfp_any());
3314 }
David Ahern0ae81332017-02-02 12:37:08 -08003315 return err;
3316}
3317
David Ahern23fb93a2018-04-17 17:33:23 -07003318static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3319{
3320 int rc = -ESRCH;
3321
3322 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3323 goto out;
3324
3325 if (cfg->fc_flags & RTF_GATEWAY &&
3326 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3327 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003328
3329 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003330out:
3331 return rc;
3332}
3333
David Ahern333c4302017-05-21 10:12:04 -06003334static int ip6_route_del(struct fib6_config *cfg,
3335 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336{
David Ahern8d1c8022018-04-17 17:33:26 -07003337 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003338 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003339 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003340 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341 int err = -ESRCH;
3342
Daniel Lezcano55786892008-03-04 13:47:47 -08003343 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003344 if (!table) {
3345 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003346 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003347 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003348
Wei Wang66f5d6c2017-10-06 12:06:10 -07003349 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003350
3351 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003352 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003353 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003354 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003355
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003357 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003358 struct fib6_nh *nh;
3359
Wei Wang2b760fc2017-10-06 12:06:03 -07003360 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003361 struct fib6_result res = {
3362 .f6i = rt,
3363 };
David Ahern23fb93a2018-04-17 17:33:23 -07003364 int rc;
3365
David Ahern7e4b5122019-04-16 14:36:00 -07003366 rt_cache = rt6_find_cached_rt(&res,
3367 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003368 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003369 if (rt_cache) {
3370 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003371 if (rc != -ESRCH) {
3372 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003373 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003374 }
David Ahern23fb93a2018-04-17 17:33:23 -07003375 }
3376 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003377 }
David Ahernad1601a2019-03-27 20:53:56 -07003378
3379 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003380 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003381 (!nh->fib_nh_dev ||
3382 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003384 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003385 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003386 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003387 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003389 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003390 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003391 if (!fib6_info_hold_safe(rt))
3392 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003393 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394
David Ahern0ae81332017-02-02 12:37:08 -08003395 /* if gateway was specified only delete the one hop */
3396 if (cfg->fc_flags & RTF_GATEWAY)
3397 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3398
3399 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 }
3401 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003402 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003403
3404 return err;
3405}
3406
David S. Miller6700c272012-07-17 03:29:28 -07003407static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003408{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003409 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003410 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003411 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003412 struct ndisc_options ndopts;
3413 struct inet6_dev *in6_dev;
3414 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003415 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003416 int optlen, on_link;
3417 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003418
Simon Horman29a3cad2013-05-28 20:34:26 +00003419 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003420 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003421
3422 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003423 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003424 return;
3425 }
3426
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003427 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003428
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003429 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003430 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003431 return;
3432 }
3433
David S. Miller6e157b62012-07-12 00:05:02 -07003434 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003435 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003436 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003437 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003438 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003439 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003440 return;
3441 }
3442
3443 in6_dev = __in6_dev_get(skb->dev);
3444 if (!in6_dev)
3445 return;
3446 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3447 return;
3448
3449 /* RFC2461 8.1:
3450 * The IP source address of the Redirect MUST be the same as the current
3451 * first-hop router for the specified ICMP Destination Address.
3452 */
3453
Alexander Aringf997c552016-06-15 21:20:23 +02003454 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003455 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3456 return;
3457 }
David S. Miller6e157b62012-07-12 00:05:02 -07003458
3459 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003460 if (ndopts.nd_opts_tgt_lladdr) {
3461 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3462 skb->dev);
3463 if (!lladdr) {
3464 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3465 return;
3466 }
3467 }
3468
David S. Miller6e157b62012-07-12 00:05:02 -07003469 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003470 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003471 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3472 return;
3473 }
3474
3475 /* Redirect received -> path was valid.
3476 * Look, redirects are sent only in response to data packets,
3477 * so that this nexthop apparently is reachable. --ANK
3478 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003479 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003480
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003481 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003482 if (!neigh)
3483 return;
3484
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485 /*
3486 * We have finally decided to accept it.
3487 */
3488
Alexander Aringf997c552016-06-15 21:20:23 +02003489 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3491 NEIGH_UPDATE_F_OVERRIDE|
3492 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003493 NEIGH_UPDATE_F_ISROUTER)),
3494 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003495
David Ahern4d85cd02018-04-20 15:37:59 -07003496 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003497 res.f6i = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003498 /* This fib6_info_hold() is safe here because we hold reference to rt
3499 * and rt already holds reference to fib6_info.
3500 */
David Ahern85bd05d2019-04-16 14:36:01 -07003501 fib6_info_hold(res.f6i);
David Ahern4d85cd02018-04-20 15:37:59 -07003502 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003503
David Ahern85bd05d2019-04-16 14:36:01 -07003504 res.nh = &res.f6i->fib6_nh;
David Ahern7d21fec2019-04-16 14:36:11 -07003505 res.fib6_flags = res.f6i->fib6_flags;
3506 res.fib6_type = res.f6i->fib6_type;
David Ahern85bd05d2019-04-16 14:36:01 -07003507 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003508 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509 goto out;
3510
3511 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3512 if (on_link)
3513 nrt->rt6i_flags &= ~RTF_GATEWAY;
3514
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003515 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516
Wei Wang2b760fc2017-10-06 12:06:03 -07003517 /* No need to remove rt from the exception table if rt is
3518 * a cached route because rt6_insert_exception() will
3519 * takes care of it
3520 */
David Ahern5012f0a2019-04-16 14:36:05 -07003521 if (rt6_insert_exception(nrt, &res)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003522 dst_release_immediate(&nrt->dst);
3523 goto out;
3524 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003525
Changli Gaod8d1f302010-06-10 23:31:35 -07003526 netevent.old = &rt->dst;
3527 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003528 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003529 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003530 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3531
Linus Torvalds1da177e2005-04-16 15:20:36 -07003532out:
David Ahern85bd05d2019-04-16 14:36:01 -07003533 fib6_info_release(res.f6i);
David S. Millere8599ff2012-07-11 23:43:53 -07003534 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003535}
3536
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003537#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003538static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003539 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003540 const struct in6_addr *gwaddr,
3541 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003542{
David Ahern830218c2016-10-24 10:52:35 -07003543 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3544 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003545 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003546 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003547 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003548
David Ahern830218c2016-10-24 10:52:35 -07003549 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003550 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003551 return NULL;
3552
Wei Wang66f5d6c2017-10-06 12:06:10 -07003553 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003554 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003555 if (!fn)
3556 goto out;
3557
Wei Wang66f5d6c2017-10-06 12:06:10 -07003558 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003559 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003560 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003561 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003562 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003563 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003564 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003565 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003566 if (!fib6_info_hold_safe(rt))
3567 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003568 break;
3569 }
3570out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003571 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003572 return rt;
3573}
3574
David Ahern8d1c8022018-04-17 17:33:26 -07003575static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003576 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003577 const struct in6_addr *gwaddr,
3578 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003579 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003580{
Thomas Graf86872cb2006-08-22 00:01:08 -07003581 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003582 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003583 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003584 .fc_dst_len = prefixlen,
3585 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3586 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003587 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003588 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003589 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003590 .fc_nlinfo.nlh = NULL,
3591 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003592 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003593
David Ahern830218c2016-10-24 10:52:35 -07003594 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003595 cfg.fc_dst = *prefix;
3596 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003597
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003598 /* We should treat it as a default route if prefix length is 0. */
3599 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003600 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003601
David Ahernacb54e32018-04-17 17:33:22 -07003602 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003603
David Ahern830218c2016-10-24 10:52:35 -07003604 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003605}
3606#endif
3607
David Ahern8d1c8022018-04-17 17:33:26 -07003608struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003609 const struct in6_addr *addr,
3610 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003611{
David Ahern830218c2016-10-24 10:52:35 -07003612 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003613 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003614 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615
David Ahernafb1d4b52018-04-17 17:33:11 -07003616 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003617 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003618 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619
Wei Wang66f5d6c2017-10-06 12:06:10 -07003620 rcu_read_lock();
3621 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003622 struct fib6_nh *nh = &rt->fib6_nh;
3623
3624 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003625 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003626 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627 break;
3628 }
Wei Wange873e4b2018-07-21 20:56:32 -07003629 if (rt && !fib6_info_hold_safe(rt))
3630 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003631 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632 return rt;
3633}
3634
David Ahern8d1c8022018-04-17 17:33:26 -07003635struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003636 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003637 struct net_device *dev,
3638 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639{
Thomas Graf86872cb2006-08-22 00:01:08 -07003640 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003641 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003642 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003643 .fc_ifindex = dev->ifindex,
3644 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3645 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003646 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003647 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003648 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003649 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003650 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003651 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003653 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654
David Ahernacb54e32018-04-17 17:33:22 -07003655 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003656 struct fib6_table *table;
3657
3658 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3659 if (table)
3660 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3661 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003662
David Ahernafb1d4b52018-04-17 17:33:11 -07003663 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664}
3665
David Ahernafb1d4b52018-04-17 17:33:11 -07003666static void __rt6_purge_dflt_routers(struct net *net,
3667 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668{
David Ahern8d1c8022018-04-17 17:33:26 -07003669 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003670
3671restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003672 rcu_read_lock();
3673 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003674 struct net_device *dev = fib6_info_nh_dev(rt);
3675 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3676
David Ahern93c2fb22018-04-18 15:38:59 -07003677 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003678 (!idev || idev->cnf.accept_ra != 2) &&
3679 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003680 rcu_read_unlock();
3681 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682 goto restart;
3683 }
3684 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003685 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003686
3687 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3688}
3689
3690void rt6_purge_dflt_routers(struct net *net)
3691{
3692 struct fib6_table *table;
3693 struct hlist_head *head;
3694 unsigned int h;
3695
3696 rcu_read_lock();
3697
3698 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3699 head = &net->ipv6.fib_table_hash[h];
3700 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3701 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003702 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003703 }
3704 }
3705
3706 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707}
3708
Daniel Lezcano55786892008-03-04 13:47:47 -08003709static void rtmsg_to_fib6_config(struct net *net,
3710 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003711 struct fib6_config *cfg)
3712{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003713 *cfg = (struct fib6_config){
3714 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3715 : RT6_TABLE_MAIN,
3716 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003717 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003718 .fc_expires = rtmsg->rtmsg_info,
3719 .fc_dst_len = rtmsg->rtmsg_dst_len,
3720 .fc_src_len = rtmsg->rtmsg_src_len,
3721 .fc_flags = rtmsg->rtmsg_flags,
3722 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003723
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003724 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003725
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003726 .fc_dst = rtmsg->rtmsg_dst,
3727 .fc_src = rtmsg->rtmsg_src,
3728 .fc_gateway = rtmsg->rtmsg_gateway,
3729 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003730}
3731
Daniel Lezcano55786892008-03-04 13:47:47 -08003732int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733{
Thomas Graf86872cb2006-08-22 00:01:08 -07003734 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003735 struct in6_rtmsg rtmsg;
3736 int err;
3737
Ian Morris67ba4152014-08-24 21:53:10 +01003738 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739 case SIOCADDRT: /* Add a route */
3740 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003741 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742 return -EPERM;
3743 err = copy_from_user(&rtmsg, arg,
3744 sizeof(struct in6_rtmsg));
3745 if (err)
3746 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003747
Daniel Lezcano55786892008-03-04 13:47:47 -08003748 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003749
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 rtnl_lock();
3751 switch (cmd) {
3752 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003753 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003754 break;
3755 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003756 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 break;
3758 default:
3759 err = -EINVAL;
3760 }
3761 rtnl_unlock();
3762
3763 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003764 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765
3766 return -EINVAL;
3767}
3768
3769/*
3770 * Drop the packet on the floor
3771 */
3772
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003773static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003774{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003775 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003776 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003777 switch (ipstats_mib_noroutes) {
3778 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003779 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003780 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003781 IP6_INC_STATS(dev_net(dst->dev),
3782 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003783 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003784 break;
3785 }
3786 /* FALLTHROUGH */
3787 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003788 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3789 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003790 break;
3791 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003792 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793 kfree_skb(skb);
3794 return 0;
3795}
3796
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003797static int ip6_pkt_discard(struct sk_buff *skb)
3798{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003799 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003800}
3801
Eric W. Biedermanede20592015-10-07 16:48:47 -05003802static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803{
Eric Dumazetadf30902009-06-02 05:19:30 +00003804 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003805 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806}
3807
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003808static int ip6_pkt_prohibit(struct sk_buff *skb)
3809{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003810 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003811}
3812
Eric W. Biedermanede20592015-10-07 16:48:47 -05003813static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003814{
Eric Dumazetadf30902009-06-02 05:19:30 +00003815 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003816 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003817}
3818
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819/*
3820 * Allocate a dst for local (unicast / anycast) address.
3821 */
3822
David Ahern360a9882018-04-18 15:39:00 -07003823struct fib6_info *addrconf_f6i_alloc(struct net *net,
3824 struct inet6_dev *idev,
3825 const struct in6_addr *addr,
3826 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827{
David Ahernc7a1ce32019-03-21 05:21:35 -07003828 struct fib6_config cfg = {
3829 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3830 .fc_ifindex = idev->dev->ifindex,
3831 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3832 .fc_dst = *addr,
3833 .fc_dst_len = 128,
3834 .fc_protocol = RTPROT_KERNEL,
3835 .fc_nlinfo.nl_net = net,
3836 .fc_ignore_dev_down = true,
3837 };
David Ahern5f02ce242016-09-10 12:09:54 -07003838
David Aherne8478e82018-04-17 17:33:13 -07003839 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003840 cfg.fc_type = RTN_ANYCAST;
3841 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003842 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003843 cfg.fc_type = RTN_LOCAL;
3844 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003846
David Ahernc7a1ce32019-03-21 05:21:35 -07003847 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003848}
3849
Daniel Walterc3968a82011-04-13 21:10:57 +00003850/* remove deleted ip from prefsrc entries */
3851struct arg_dev_net_ip {
3852 struct net_device *dev;
3853 struct net *net;
3854 struct in6_addr *addr;
3855};
3856
David Ahern8d1c8022018-04-17 17:33:26 -07003857static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003858{
3859 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3860 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3861 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3862
David Ahernad1601a2019-03-27 20:53:56 -07003863 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003864 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003865 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003866 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003867 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003868 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003869 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003870 }
3871 return 0;
3872}
3873
3874void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3875{
3876 struct net *net = dev_net(ifp->idev->dev);
3877 struct arg_dev_net_ip adni = {
3878 .dev = ifp->idev->dev,
3879 .net = net,
3880 .addr = &ifp->addr,
3881 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003882 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003883}
3884
David Ahern2b2450c2019-03-27 20:53:52 -07003885#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003886
3887/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003888static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003889{
3890 struct in6_addr *gateway = (struct in6_addr *)arg;
3891
David Ahern93c2fb22018-04-18 15:38:59 -07003892 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003893 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003894 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003895 return -1;
3896 }
Wei Wangb16cb452017-10-06 12:06:00 -07003897
3898 /* Further clean up cached routes in exception table.
3899 * This is needed because cached route may have a different
3900 * gateway than its 'parent' in the case of an ip redirect.
3901 */
3902 rt6_exceptions_clean_tohost(rt, gateway);
3903
Duan Jiongbe7a0102014-05-15 15:56:14 +08003904 return 0;
3905}
3906
3907void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3908{
3909 fib6_clean_all(net, fib6_clean_tohost, gateway);
3910}
3911
Ido Schimmel2127d952018-01-07 12:45:03 +02003912struct arg_netdev_event {
3913 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003914 union {
3915 unsigned int nh_flags;
3916 unsigned long event;
3917 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003918};
3919
David Ahern8d1c8022018-04-17 17:33:26 -07003920static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003921{
David Ahern8d1c8022018-04-17 17:33:26 -07003922 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003923 struct fib6_node *fn;
3924
David Ahern93c2fb22018-04-18 15:38:59 -07003925 fn = rcu_dereference_protected(rt->fib6_node,
3926 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003927 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003928 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003929 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003930 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003931 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003932 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003933 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003934 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003935 }
3936
3937 return NULL;
3938}
3939
David Ahern8d1c8022018-04-17 17:33:26 -07003940static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003941{
David Ahernad1601a2019-03-27 20:53:56 -07003942 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3943 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3944 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003945 return true;
3946
3947 return false;
3948}
3949
David Ahern8d1c8022018-04-17 17:33:26 -07003950static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003951{
David Ahern8d1c8022018-04-17 17:33:26 -07003952 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003953 int total = 0;
3954
3955 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003956 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003957
David Ahern93c2fb22018-04-18 15:38:59 -07003958 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003959 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003960 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003961 }
3962
3963 return total;
3964}
3965
David Ahern8d1c8022018-04-17 17:33:26 -07003966static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003967{
3968 int upper_bound = -1;
3969
3970 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003971 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003972 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3973 total) - 1;
3974 }
David Ahernad1601a2019-03-27 20:53:56 -07003975 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003976}
3977
David Ahern8d1c8022018-04-17 17:33:26 -07003978static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003979{
David Ahern8d1c8022018-04-17 17:33:26 -07003980 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003981 int weight = 0;
3982
3983 rt6_upper_bound_set(rt, &weight, total);
3984
David Ahern93c2fb22018-04-18 15:38:59 -07003985 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003986 rt6_upper_bound_set(iter, &weight, total);
3987}
3988
David Ahern8d1c8022018-04-17 17:33:26 -07003989void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003990{
David Ahern8d1c8022018-04-17 17:33:26 -07003991 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003992 int total;
3993
3994 /* In case the entire multipath route was marked for flushing,
3995 * then there is no need to rebalance upon the removal of every
3996 * sibling route.
3997 */
David Ahern93c2fb22018-04-18 15:38:59 -07003998 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003999 return;
4000
4001 /* During lookup routes are evaluated in order, so we need to
4002 * make sure upper bounds are assigned from the first sibling
4003 * onwards.
4004 */
4005 first = rt6_multipath_first_sibling(rt);
4006 if (WARN_ON_ONCE(!first))
4007 return;
4008
4009 total = rt6_multipath_total_weight(first);
4010 rt6_multipath_upper_bound_set(first, total);
4011}
4012
David Ahern8d1c8022018-04-17 17:33:26 -07004013static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02004014{
4015 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07004016 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02004017
David Ahernad1601a2019-03-27 20:53:56 -07004018 if (rt != net->ipv6.fib6_null_entry &&
4019 rt->fib6_nh.fib_nh_dev == arg->dev) {
4020 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07004021 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004022 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004023 }
Ido Schimmel2127d952018-01-07 12:45:03 +02004024
4025 return 0;
4026}
4027
4028void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
4029{
4030 struct arg_netdev_event arg = {
4031 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004032 {
4033 .nh_flags = nh_flags,
4034 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004035 };
4036
4037 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4038 arg.nh_flags |= RTNH_F_LINKDOWN;
4039
4040 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4041}
4042
David Ahern8d1c8022018-04-17 17:33:26 -07004043static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004044 const struct net_device *dev)
4045{
David Ahern8d1c8022018-04-17 17:33:26 -07004046 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004047
David Ahernad1601a2019-03-27 20:53:56 -07004048 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004049 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004050 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004051 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004052 return true;
4053
4054 return false;
4055}
4056
David Ahern8d1c8022018-04-17 17:33:26 -07004057static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004058{
David Ahern8d1c8022018-04-17 17:33:26 -07004059 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004060
4061 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004062 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004063 iter->should_flush = 1;
4064}
4065
David Ahern8d1c8022018-04-17 17:33:26 -07004066static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004067 const struct net_device *down_dev)
4068{
David Ahern8d1c8022018-04-17 17:33:26 -07004069 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004070 unsigned int dead = 0;
4071
David Ahernad1601a2019-03-27 20:53:56 -07004072 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4073 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004074 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004075 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004076 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4077 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004078 dead++;
4079
4080 return dead;
4081}
4082
David Ahern8d1c8022018-04-17 17:33:26 -07004083static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004084 const struct net_device *dev,
4085 unsigned int nh_flags)
4086{
David Ahern8d1c8022018-04-17 17:33:26 -07004087 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004088
David Ahernad1601a2019-03-27 20:53:56 -07004089 if (rt->fib6_nh.fib_nh_dev == dev)
4090 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004091 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004092 if (iter->fib6_nh.fib_nh_dev == dev)
4093 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004094}
4095
David Aherna1a22c12017-01-18 07:40:36 -08004096/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004097static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004098{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004099 const struct arg_netdev_event *arg = p_arg;
4100 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004101 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004102
David Ahern421842e2018-04-17 17:33:18 -07004103 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004104 return 0;
4105
4106 switch (arg->event) {
4107 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004108 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004109 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004110 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004111 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004112 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004113 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004114 if (rt6_multipath_uses_dev(rt, dev)) {
4115 unsigned int count;
4116
4117 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004118 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004119 rt6_multipath_flush(rt);
4120 return -1;
4121 }
4122 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4123 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004124 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004125 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004126 }
4127 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004128 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004129 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004130 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004131 break;
David Ahernad1601a2019-03-27 20:53:56 -07004132 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004133 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004134 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004135 }
David S. Millerc159d302011-12-26 15:24:36 -05004136
Linus Torvalds1da177e2005-04-16 15:20:36 -07004137 return 0;
4138}
4139
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004140void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004141{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004142 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004143 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004144 {
4145 .event = event,
4146 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004147 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004148 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004149
David Ahern7c6bb7d2018-10-11 20:17:21 -07004150 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4151 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4152 else
4153 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004154}
4155
4156void rt6_disable_ip(struct net_device *dev, unsigned long event)
4157{
4158 rt6_sync_down_dev(dev, event);
4159 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4160 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161}
4162
Eric Dumazet95c96172012-04-15 05:58:06 +00004163struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004165 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004166};
4167
David Ahern8d1c8022018-04-17 17:33:26 -07004168static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004169{
4170 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4171 struct inet6_dev *idev;
4172
4173 /* In IPv6 pmtu discovery is not optional,
4174 so that RTAX_MTU lock cannot disable it.
4175 We still use this lock to block changes
4176 caused by addrconf/ndisc.
4177 */
4178
4179 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004180 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004181 return 0;
4182
4183 /* For administrative MTU increase, there is no way to discover
4184 IPv6 PMTU increase, so PMTU increase should be updated here.
4185 Since RFC 1981 doesn't include administrative MTU increase
4186 update PMTU increase is a MUST. (i.e. jumbo frame)
4187 */
David Ahernad1601a2019-03-27 20:53:56 -07004188 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004189 !fib6_metric_locked(rt, RTAX_MTU)) {
4190 u32 mtu = rt->fib6_pmtu;
4191
4192 if (mtu >= arg->mtu ||
4193 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4194 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4195
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004196 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004197 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004198 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004199 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004200 return 0;
4201}
4202
Eric Dumazet95c96172012-04-15 05:58:06 +00004203void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004204{
Thomas Grafc71099a2006-08-04 23:20:06 -07004205 struct rt6_mtu_change_arg arg = {
4206 .dev = dev,
4207 .mtu = mtu,
4208 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209
Li RongQing0c3584d2013-12-27 16:32:38 +08004210 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004211}
4212
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004213static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004214 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004215 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004216 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004217 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004218 [RTA_PRIORITY] = { .type = NLA_U32 },
4219 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004220 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004221 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004222 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4223 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004224 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004225 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004226 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004227 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004228 [RTA_IP_PROTO] = { .type = NLA_U8 },
4229 [RTA_SPORT] = { .type = NLA_U16 },
4230 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004231};
4232
4233static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004234 struct fib6_config *cfg,
4235 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004236{
Thomas Graf86872cb2006-08-22 00:01:08 -07004237 struct rtmsg *rtm;
4238 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004239 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004240 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004241
Johannes Bergfceb6432017-04-12 14:34:07 +02004242 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004243 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004244 if (err < 0)
4245 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004246
Thomas Graf86872cb2006-08-22 00:01:08 -07004247 err = -EINVAL;
4248 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004249
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004250 *cfg = (struct fib6_config){
4251 .fc_table = rtm->rtm_table,
4252 .fc_dst_len = rtm->rtm_dst_len,
4253 .fc_src_len = rtm->rtm_src_len,
4254 .fc_flags = RTF_UP,
4255 .fc_protocol = rtm->rtm_protocol,
4256 .fc_type = rtm->rtm_type,
4257
4258 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4259 .fc_nlinfo.nlh = nlh,
4260 .fc_nlinfo.nl_net = sock_net(skb->sk),
4261 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004262
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004263 if (rtm->rtm_type == RTN_UNREACHABLE ||
4264 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004265 rtm->rtm_type == RTN_PROHIBIT ||
4266 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004267 cfg->fc_flags |= RTF_REJECT;
4268
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004269 if (rtm->rtm_type == RTN_LOCAL)
4270 cfg->fc_flags |= RTF_LOCAL;
4271
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004272 if (rtm->rtm_flags & RTM_F_CLONED)
4273 cfg->fc_flags |= RTF_CACHE;
4274
David Ahernfc1e64e2018-01-25 16:55:09 -08004275 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4276
Thomas Graf86872cb2006-08-22 00:01:08 -07004277 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004278 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004279 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004280 }
David Aherne3818542019-02-26 09:00:03 -08004281 if (tb[RTA_VIA]) {
4282 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4283 goto errout;
4284 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004285
4286 if (tb[RTA_DST]) {
4287 int plen = (rtm->rtm_dst_len + 7) >> 3;
4288
4289 if (nla_len(tb[RTA_DST]) < plen)
4290 goto errout;
4291
4292 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004293 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004294
4295 if (tb[RTA_SRC]) {
4296 int plen = (rtm->rtm_src_len + 7) >> 3;
4297
4298 if (nla_len(tb[RTA_SRC]) < plen)
4299 goto errout;
4300
4301 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004302 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004303
Daniel Walterc3968a82011-04-13 21:10:57 +00004304 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004305 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004306
Thomas Graf86872cb2006-08-22 00:01:08 -07004307 if (tb[RTA_OIF])
4308 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4309
4310 if (tb[RTA_PRIORITY])
4311 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4312
4313 if (tb[RTA_METRICS]) {
4314 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4315 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004316 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004317
4318 if (tb[RTA_TABLE])
4319 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4320
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004321 if (tb[RTA_MULTIPATH]) {
4322 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4323 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004324
4325 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004326 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004327 if (err < 0)
4328 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004329 }
4330
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004331 if (tb[RTA_PREF]) {
4332 pref = nla_get_u8(tb[RTA_PREF]);
4333 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4334 pref != ICMPV6_ROUTER_PREF_HIGH)
4335 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4336 cfg->fc_flags |= RTF_PREF(pref);
4337 }
4338
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004339 if (tb[RTA_ENCAP])
4340 cfg->fc_encap = tb[RTA_ENCAP];
4341
David Ahern9ed59592017-01-17 14:57:36 -08004342 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004343 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4344
David Ahernc255bd62017-05-27 16:19:27 -06004345 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004346 if (err < 0)
4347 goto errout;
4348 }
4349
Xin Long32bc2012015-12-16 17:50:11 +08004350 if (tb[RTA_EXPIRES]) {
4351 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4352
4353 if (addrconf_finite_timeout(timeout)) {
4354 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4355 cfg->fc_flags |= RTF_EXPIRES;
4356 }
4357 }
4358
Thomas Graf86872cb2006-08-22 00:01:08 -07004359 err = 0;
4360errout:
4361 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004362}
4363
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004364struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004365 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004366 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004367 struct list_head next;
4368};
4369
David Ahernd4ead6b2018-04-17 17:33:16 -07004370static int ip6_route_info_append(struct net *net,
4371 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004372 struct fib6_info *rt,
4373 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004374{
4375 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004376 int err = -EEXIST;
4377
4378 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004379 /* check if fib6_info already exists */
4380 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004381 return err;
4382 }
4383
4384 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4385 if (!nh)
4386 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004387 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004388 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4389 list_add_tail(&nh->next, rt6_nh_list);
4390
4391 return 0;
4392}
4393
David Ahern8d1c8022018-04-17 17:33:26 -07004394static void ip6_route_mpath_notify(struct fib6_info *rt,
4395 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004396 struct nl_info *info,
4397 __u16 nlflags)
4398{
4399 /* if this is an APPEND route, then rt points to the first route
4400 * inserted and rt_last points to last route inserted. Userspace
4401 * wants a consistent dump of the route which starts at the first
4402 * nexthop. Since sibling routes are always added at the end of
4403 * the list, find the first sibling of the last route appended
4404 */
David Ahern93c2fb22018-04-18 15:38:59 -07004405 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4406 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004407 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004408 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004409 }
4410
4411 if (rt)
4412 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4413}
4414
David Ahern333c4302017-05-21 10:12:04 -06004415static int ip6_route_multipath_add(struct fib6_config *cfg,
4416 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004417{
David Ahern8d1c8022018-04-17 17:33:26 -07004418 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004419 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004420 struct fib6_config r_cfg;
4421 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004422 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004423 struct rt6_nh *err_nh;
4424 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004425 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004426 int remaining;
4427 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004428 int err = 1;
4429 int nhn = 0;
4430 int replace = (cfg->fc_nlinfo.nlh &&
4431 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4432 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004433
David Ahern3b1137f2017-02-02 12:37:10 -08004434 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4435 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4436 nlflags |= NLM_F_APPEND;
4437
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004438 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004439 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004440
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004441 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004442 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004443 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004444 while (rtnh_ok(rtnh, remaining)) {
4445 memcpy(&r_cfg, cfg, sizeof(*cfg));
4446 if (rtnh->rtnh_ifindex)
4447 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4448
4449 attrlen = rtnh_attrlen(rtnh);
4450 if (attrlen > 0) {
4451 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4452
4453 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4454 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004455 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004456 r_cfg.fc_flags |= RTF_GATEWAY;
4457 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004458 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4459 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4460 if (nla)
4461 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004462 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004463
David Ahern68e2ffd2018-03-20 10:06:59 -07004464 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004465 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004466 if (IS_ERR(rt)) {
4467 err = PTR_ERR(rt);
4468 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004469 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004470 }
David Ahernb5d2d752018-07-15 09:35:19 -07004471 if (!rt6_qualify_for_ecmp(rt)) {
4472 err = -EINVAL;
4473 NL_SET_ERR_MSG(extack,
4474 "Device only routes can not be added for IPv6 using the multipath API.");
4475 fib6_info_release(rt);
4476 goto cleanup;
4477 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004478
David Ahernad1601a2019-03-27 20:53:56 -07004479 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004480
David Ahernd4ead6b2018-04-17 17:33:16 -07004481 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4482 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004483 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004484 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004485 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004486 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004487
4488 rtnh = rtnh_next(rtnh, &remaining);
4489 }
4490
David Ahern3b1137f2017-02-02 12:37:10 -08004491 /* for add and replace send one notification with all nexthops.
4492 * Skip the notification in fib6_add_rt2node and send one with
4493 * the full route when done
4494 */
4495 info->skip_notify = 1;
4496
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004497 err_nh = NULL;
4498 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004499 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4500 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004501
David Ahernf7225172018-06-04 13:41:42 -07004502 if (!err) {
4503 /* save reference to last route successfully inserted */
4504 rt_last = nh->fib6_info;
4505
4506 /* save reference to first route for notification */
4507 if (!rt_notif)
4508 rt_notif = nh->fib6_info;
4509 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004510
David Ahern8d1c8022018-04-17 17:33:26 -07004511 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4512 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004513 if (err) {
4514 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004515 NL_SET_ERR_MSG_MOD(extack,
4516 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004517 err_nh = nh;
4518 goto add_errout;
4519 }
4520
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004521 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004522 * these flags after the first nexthop: if there is a collision,
4523 * we have already failed to add the first nexthop:
4524 * fib6_add_rt2node() has rejected it; when replacing, old
4525 * nexthops have been replaced by first new, the rest should
4526 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004527 */
Michal Kubeček27596472015-05-18 20:54:00 +02004528 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4529 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004530 nhn++;
4531 }
4532
David Ahern3b1137f2017-02-02 12:37:10 -08004533 /* success ... tell user about new route */
4534 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004535 goto cleanup;
4536
4537add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004538 /* send notification for routes that were added so that
4539 * the delete notifications sent by ip6_route_del are
4540 * coherent
4541 */
4542 if (rt_notif)
4543 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4544
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004545 /* Delete routes that were already added */
4546 list_for_each_entry(nh, &rt6_nh_list, next) {
4547 if (err_nh == nh)
4548 break;
David Ahern333c4302017-05-21 10:12:04 -06004549 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004550 }
4551
4552cleanup:
4553 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004554 if (nh->fib6_info)
4555 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004556 list_del(&nh->next);
4557 kfree(nh);
4558 }
4559
4560 return err;
4561}
4562
David Ahern333c4302017-05-21 10:12:04 -06004563static int ip6_route_multipath_del(struct fib6_config *cfg,
4564 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004565{
4566 struct fib6_config r_cfg;
4567 struct rtnexthop *rtnh;
4568 int remaining;
4569 int attrlen;
4570 int err = 1, last_err = 0;
4571
4572 remaining = cfg->fc_mp_len;
4573 rtnh = (struct rtnexthop *)cfg->fc_mp;
4574
4575 /* Parse a Multipath Entry */
4576 while (rtnh_ok(rtnh, remaining)) {
4577 memcpy(&r_cfg, cfg, sizeof(*cfg));
4578 if (rtnh->rtnh_ifindex)
4579 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4580
4581 attrlen = rtnh_attrlen(rtnh);
4582 if (attrlen > 0) {
4583 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4584
4585 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4586 if (nla) {
4587 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4588 r_cfg.fc_flags |= RTF_GATEWAY;
4589 }
4590 }
David Ahern333c4302017-05-21 10:12:04 -06004591 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004592 if (err)
4593 last_err = err;
4594
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004595 rtnh = rtnh_next(rtnh, &remaining);
4596 }
4597
4598 return last_err;
4599}
4600
David Ahernc21ef3e2017-04-16 09:48:24 -07004601static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4602 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004603{
Thomas Graf86872cb2006-08-22 00:01:08 -07004604 struct fib6_config cfg;
4605 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004606
David Ahern333c4302017-05-21 10:12:04 -06004607 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004608 if (err < 0)
4609 return err;
4610
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004611 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004612 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004613 else {
4614 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004615 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004617}
4618
David Ahernc21ef3e2017-04-16 09:48:24 -07004619static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4620 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004621{
Thomas Graf86872cb2006-08-22 00:01:08 -07004622 struct fib6_config cfg;
4623 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004624
David Ahern333c4302017-05-21 10:12:04 -06004625 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004626 if (err < 0)
4627 return err;
4628
David Ahern67f69512019-03-21 05:21:34 -07004629 if (cfg.fc_metric == 0)
4630 cfg.fc_metric = IP6_RT_PRIO_USER;
4631
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004632 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004633 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004634 else
David Ahernacb54e32018-04-17 17:33:22 -07004635 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004636}
4637
David Ahern8d1c8022018-04-17 17:33:26 -07004638static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004639{
David Ahernbeb1afac52017-02-02 12:37:09 -08004640 int nexthop_len = 0;
4641
David Ahern93c2fb22018-04-18 15:38:59 -07004642 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004643 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4644 + NLA_ALIGN(sizeof(struct rtnexthop))
4645 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004646 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004647
David Ahern93c2fb22018-04-18 15:38:59 -07004648 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004649 }
4650
Thomas Graf339bf982006-11-10 14:10:15 -08004651 return NLMSG_ALIGN(sizeof(struct rtmsg))
4652 + nla_total_size(16) /* RTA_SRC */
4653 + nla_total_size(16) /* RTA_DST */
4654 + nla_total_size(16) /* RTA_GATEWAY */
4655 + nla_total_size(16) /* RTA_PREFSRC */
4656 + nla_total_size(4) /* RTA_TABLE */
4657 + nla_total_size(4) /* RTA_IIF */
4658 + nla_total_size(4) /* RTA_OIF */
4659 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004660 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004661 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004662 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004663 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004664 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004665 + nexthop_len;
4666}
4667
David Ahernd4ead6b2018-04-17 17:33:16 -07004668static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004669 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004670 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004671 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004672 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004673{
Xin Long22d0bd82018-09-11 14:33:58 +08004674 struct rt6_info *rt6 = (struct rt6_info *)dst;
4675 struct rt6key *rt6_dst, *rt6_src;
4676 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004677 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004678 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004679 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680
Eric W. Biederman15e47302012-09-07 20:12:54 +00004681 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004682 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004683 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004684
Xin Long22d0bd82018-09-11 14:33:58 +08004685 if (rt6) {
4686 rt6_dst = &rt6->rt6i_dst;
4687 rt6_src = &rt6->rt6i_src;
4688 rt6_flags = rt6->rt6i_flags;
4689 } else {
4690 rt6_dst = &rt->fib6_dst;
4691 rt6_src = &rt->fib6_src;
4692 rt6_flags = rt->fib6_flags;
4693 }
4694
Thomas Graf2d7202b2006-08-22 00:01:27 -07004695 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004697 rtm->rtm_dst_len = rt6_dst->plen;
4698 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004699 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004700 if (rt->fib6_table)
4701 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004702 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004703 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004704 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004705 if (nla_put_u32(skb, RTA_TABLE, table))
4706 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004707
4708 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004709 rtm->rtm_flags = 0;
4710 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004711 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004712
Xin Long22d0bd82018-09-11 14:33:58 +08004713 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004714 rtm->rtm_flags |= RTM_F_CLONED;
4715
David Ahernd4ead6b2018-04-17 17:33:16 -07004716 if (dest) {
4717 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004718 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004719 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004720 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004721 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004722 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723#ifdef CONFIG_IPV6_SUBTREES
4724 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004725 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004726 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004727 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004728 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004729 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004730 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004732 if (iif) {
4733#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004734 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004735 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004736
David Ahernfd61c6b2017-01-17 15:51:07 -08004737 if (err == 0)
4738 return 0;
4739 if (err < 0)
4740 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004741 } else
4742#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004743 if (nla_put_u32(skb, RTA_IIF, iif))
4744 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004745 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004746 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004747 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004748 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004749 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004750 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004751
David Ahern93c2fb22018-04-18 15:38:59 -07004752 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004753 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004754 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004755 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004756 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004757 }
4758
David Ahernd4ead6b2018-04-17 17:33:16 -07004759 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4760 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004761 goto nla_put_failure;
4762
David Ahern93c2fb22018-04-18 15:38:59 -07004763 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004764 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004765
David Ahernbeb1afac52017-02-02 12:37:09 -08004766 /* For multipath routes, walk the siblings list and add
4767 * each as a nexthop within RTA_MULTIPATH.
4768 */
Xin Long22d0bd82018-09-11 14:33:58 +08004769 if (rt6) {
4770 if (rt6_flags & RTF_GATEWAY &&
4771 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4772 goto nla_put_failure;
4773
4774 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4775 goto nla_put_failure;
4776 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004777 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004778 struct nlattr *mp;
4779
4780 mp = nla_nest_start(skb, RTA_MULTIPATH);
4781 if (!mp)
4782 goto nla_put_failure;
4783
David Ahernc0a72072019-04-02 14:11:58 -07004784 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4785 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004786 goto nla_put_failure;
4787
4788 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004789 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004790 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4791 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004792 goto nla_put_failure;
4793 }
4794
4795 nla_nest_end(skb, mp);
4796 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004797 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4798 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004799 goto nla_put_failure;
4800 }
4801
Xin Long22d0bd82018-09-11 14:33:58 +08004802 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004803 expires = dst ? dst->expires : rt->expires;
4804 expires -= jiffies;
4805 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004806
David Ahernd4ead6b2018-04-17 17:33:16 -07004807 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004808 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004809
Xin Long22d0bd82018-09-11 14:33:58 +08004810 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004811 goto nla_put_failure;
4812
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004813
Johannes Berg053c0952015-01-16 22:09:00 +01004814 nlmsg_end(skb, nlh);
4815 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004816
4817nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004818 nlmsg_cancel(skb, nlh);
4819 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004820}
4821
David Ahern13e38902018-10-15 18:56:44 -07004822static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4823 const struct net_device *dev)
4824{
David Ahernad1601a2019-03-27 20:53:56 -07004825 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004826 return true;
4827
4828 if (f6i->fib6_nsiblings) {
4829 struct fib6_info *sibling, *next_sibling;
4830
4831 list_for_each_entry_safe(sibling, next_sibling,
4832 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004833 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004834 return true;
4835 }
4836 }
4837
4838 return false;
4839}
4840
David Ahern8d1c8022018-04-17 17:33:26 -07004841int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004842{
4843 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004844 struct fib_dump_filter *filter = &arg->filter;
4845 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004846 struct net *net = arg->net;
4847
David Ahern421842e2018-04-17 17:33:18 -07004848 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004849 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004850
David Ahern13e38902018-10-15 18:56:44 -07004851 if ((filter->flags & RTM_F_PREFIX) &&
4852 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4853 /* success since this is not a prefix route */
4854 return 1;
4855 }
4856 if (filter->filter_set) {
4857 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4858 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4859 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004860 return 1;
4861 }
David Ahern13e38902018-10-15 18:56:44 -07004862 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004863 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004864
David Ahernd4ead6b2018-04-17 17:33:16 -07004865 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4866 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004867 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004868}
4869
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004870static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4871 const struct nlmsghdr *nlh,
4872 struct nlattr **tb,
4873 struct netlink_ext_ack *extack)
4874{
4875 struct rtmsg *rtm;
4876 int i, err;
4877
4878 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4879 NL_SET_ERR_MSG_MOD(extack,
4880 "Invalid header for get route request");
4881 return -EINVAL;
4882 }
4883
4884 if (!netlink_strict_get_check(skb))
4885 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4886 rtm_ipv6_policy, extack);
4887
4888 rtm = nlmsg_data(nlh);
4889 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4890 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4891 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4892 rtm->rtm_type) {
4893 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4894 return -EINVAL;
4895 }
4896 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4897 NL_SET_ERR_MSG_MOD(extack,
4898 "Invalid flags for get route request");
4899 return -EINVAL;
4900 }
4901
4902 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4903 rtm_ipv6_policy, extack);
4904 if (err)
4905 return err;
4906
4907 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4908 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4909 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4910 return -EINVAL;
4911 }
4912
4913 for (i = 0; i <= RTA_MAX; i++) {
4914 if (!tb[i])
4915 continue;
4916
4917 switch (i) {
4918 case RTA_SRC:
4919 case RTA_DST:
4920 case RTA_IIF:
4921 case RTA_OIF:
4922 case RTA_MARK:
4923 case RTA_UID:
4924 case RTA_SPORT:
4925 case RTA_DPORT:
4926 case RTA_IP_PROTO:
4927 break;
4928 default:
4929 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4930 return -EINVAL;
4931 }
4932 }
4933
4934 return 0;
4935}
4936
David Ahernc21ef3e2017-04-16 09:48:24 -07004937static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4938 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004939{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004940 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004941 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004942 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004943 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004944 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004945 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004946 struct sk_buff *skb;
4947 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004948 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004949 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004950
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004951 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004952 if (err < 0)
4953 goto errout;
4954
4955 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004956 rtm = nlmsg_data(nlh);
4957 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004958 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004959
4960 if (tb[RTA_SRC]) {
4961 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4962 goto errout;
4963
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004964 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004965 }
4966
4967 if (tb[RTA_DST]) {
4968 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4969 goto errout;
4970
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004971 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004972 }
4973
4974 if (tb[RTA_IIF])
4975 iif = nla_get_u32(tb[RTA_IIF]);
4976
4977 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004978 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004979
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004980 if (tb[RTA_MARK])
4981 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4982
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004983 if (tb[RTA_UID])
4984 fl6.flowi6_uid = make_kuid(current_user_ns(),
4985 nla_get_u32(tb[RTA_UID]));
4986 else
4987 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4988
Roopa Prabhueacb9382018-05-22 14:03:28 -07004989 if (tb[RTA_SPORT])
4990 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4991
4992 if (tb[RTA_DPORT])
4993 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4994
4995 if (tb[RTA_IP_PROTO]) {
4996 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004997 &fl6.flowi6_proto, AF_INET6,
4998 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004999 if (err)
5000 goto errout;
5001 }
5002
Thomas Grafab364a62006-08-22 00:01:47 -07005003 if (iif) {
5004 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005005 int flags = 0;
5006
Florian Westphal121622d2017-08-15 16:34:42 +02005007 rcu_read_lock();
5008
5009 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07005010 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02005011 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07005012 err = -ENODEV;
5013 goto errout;
5014 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005015
5016 fl6.flowi6_iif = iif;
5017
5018 if (!ipv6_addr_any(&fl6.saddr))
5019 flags |= RT6_LOOKUP_F_HAS_SADDR;
5020
David Ahernb75cc8f2018-03-02 08:32:17 -08005021 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02005022
5023 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00005024 } else {
5025 fl6.flowi6_oif = oif;
5026
Ido Schimmel58acfd72017-12-20 12:28:25 +02005027 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005028 }
5029
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005030
5031 rt = container_of(dst, struct rt6_info, dst);
5032 if (rt->dst.error) {
5033 err = rt->dst.error;
5034 ip6_rt_put(rt);
5035 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005037
WANG Cong9d6acb32017-03-01 20:48:39 -08005038 if (rt == net->ipv6.ip6_null_entry) {
5039 err = rt->dst.error;
5040 ip6_rt_put(rt);
5041 goto errout;
5042 }
5043
Linus Torvalds1da177e2005-04-16 15:20:36 -07005044 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005045 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005046 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005047 err = -ENOBUFS;
5048 goto errout;
5049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005050
Changli Gaod8d1f302010-06-10 23:31:35 -07005051 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005052
5053 rcu_read_lock();
5054 from = rcu_dereference(rt->from);
5055
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005056 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005057 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005058 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5059 nlh->nlmsg_seq, 0);
5060 else
David Aherna68886a2018-04-20 15:38:02 -07005061 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5062 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005063 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5064 0);
David Aherna68886a2018-04-20 15:38:02 -07005065 rcu_read_unlock();
5066
Linus Torvalds1da177e2005-04-16 15:20:36 -07005067 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005068 kfree_skb(skb);
5069 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005070 }
5071
Eric W. Biederman15e47302012-09-07 20:12:54 +00005072 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005073errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005074 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005075}
5076
David Ahern8d1c8022018-04-17 17:33:26 -07005077void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005078 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005079{
5080 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005081 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005082 u32 seq;
5083 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005084
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005085 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005086 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005087
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005088 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005089 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005090 goto errout;
5091
David Ahernd4ead6b2018-04-17 17:33:16 -07005092 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5093 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005094 if (err < 0) {
5095 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5096 WARN_ON(err == -EMSGSIZE);
5097 kfree_skb(skb);
5098 goto errout;
5099 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005100 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005101 info->nlh, gfp_any());
5102 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005103errout:
5104 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005105 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005106}
5107
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005108static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005109 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005110{
Jiri Pirko351638e2013-05-28 01:30:21 +00005111 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005112 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005113
WANG Cong242d3a42017-05-08 10:12:13 -07005114 if (!(dev->flags & IFF_LOOPBACK))
5115 return NOTIFY_OK;
5116
5117 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005118 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005119 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005120 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5121#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005122 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005123 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005124 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005125 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5126#endif
WANG Cong76da0702017-06-20 11:42:27 -07005127 } else if (event == NETDEV_UNREGISTER &&
5128 dev->reg_state != NETREG_UNREGISTERED) {
5129 /* NETDEV_UNREGISTER could be fired for multiple times by
5130 * netdev_wait_allrefs(). Make sure we only call this once.
5131 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005132 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005133#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005134 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5135 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005136#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005137 }
5138
5139 return NOTIFY_OK;
5140}
5141
Linus Torvalds1da177e2005-04-16 15:20:36 -07005142/*
5143 * /proc
5144 */
5145
5146#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005147static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5148{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005149 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005151 net->ipv6.rt6_stats->fib_nodes,
5152 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005153 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005154 net->ipv6.rt6_stats->fib_rt_entries,
5155 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005156 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005157 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005158
5159 return 0;
5160}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005161#endif /* CONFIG_PROC_FS */
5162
5163#ifdef CONFIG_SYSCTL
5164
Linus Torvalds1da177e2005-04-16 15:20:36 -07005165static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005166int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005167 void __user *buffer, size_t *lenp, loff_t *ppos)
5168{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005169 struct net *net;
5170 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005171 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005172 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005173 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005174
5175 net = (struct net *)ctl->extra1;
5176 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005177 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5178 if (ret)
5179 return ret;
5180
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005181 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005182 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005183}
5184
David Ahern7c6bb7d2018-10-11 20:17:21 -07005185static int zero;
5186static int one = 1;
5187
David Aherned792e22018-10-08 14:06:34 -07005188static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005189 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005191 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005193 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005194 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005195 },
5196 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005197 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005198 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005199 .maxlen = sizeof(int),
5200 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005201 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005202 },
5203 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005205 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005206 .maxlen = sizeof(int),
5207 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005208 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209 },
5210 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005211 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005212 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005213 .maxlen = sizeof(int),
5214 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005215 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005216 },
5217 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005218 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005219 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005220 .maxlen = sizeof(int),
5221 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005222 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005223 },
5224 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005225 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005226 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005227 .maxlen = sizeof(int),
5228 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005229 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005230 },
5231 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005232 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005233 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005234 .maxlen = sizeof(int),
5235 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005236 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005237 },
5238 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005239 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005240 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005241 .maxlen = sizeof(int),
5242 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005243 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005244 },
5245 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005247 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005248 .maxlen = sizeof(int),
5249 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005250 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005251 },
5252 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005253 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005254 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005255 .maxlen = sizeof(int),
5256 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005257 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005258 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005259 {
5260 .procname = "skip_notify_on_dev_down",
5261 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5262 .maxlen = sizeof(int),
5263 .mode = 0644,
5264 .proc_handler = proc_dointvec,
5265 .extra1 = &zero,
5266 .extra2 = &one,
5267 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005268 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005269};
5270
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005271struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005272{
5273 struct ctl_table *table;
5274
5275 table = kmemdup(ipv6_route_table_template,
5276 sizeof(ipv6_route_table_template),
5277 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005278
5279 if (table) {
5280 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005281 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005282 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005283 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5284 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5285 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5286 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5287 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5288 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5289 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005290 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005291 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005292
5293 /* Don't export sysctls to unprivileged users */
5294 if (net->user_ns != &init_user_ns)
5295 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005296 }
5297
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005298 return table;
5299}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005300#endif
5301
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005302static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005303{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005304 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005305
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005306 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5307 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005308
Eric Dumazetfc66f952010-10-08 06:37:34 +00005309 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5310 goto out_ip6_dst_ops;
5311
David Ahern421842e2018-04-17 17:33:18 -07005312 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5313 sizeof(*net->ipv6.fib6_null_entry),
5314 GFP_KERNEL);
5315 if (!net->ipv6.fib6_null_entry)
5316 goto out_ip6_dst_entries;
5317
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005318 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5319 sizeof(*net->ipv6.ip6_null_entry),
5320 GFP_KERNEL);
5321 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005322 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005323 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005324 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5325 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005326
5327#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005328 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005329 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5330 sizeof(*net->ipv6.ip6_prohibit_entry),
5331 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005332 if (!net->ipv6.ip6_prohibit_entry)
5333 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005334 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005335 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5336 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005337
5338 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5339 sizeof(*net->ipv6.ip6_blk_hole_entry),
5340 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005341 if (!net->ipv6.ip6_blk_hole_entry)
5342 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005343 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005344 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5345 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005346#endif
5347
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005348 net->ipv6.sysctl.flush_delay = 0;
5349 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5350 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5351 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5352 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5353 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5354 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5355 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005356 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005357
Benjamin Thery6891a342008-03-04 13:49:47 -08005358 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5359
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005360 ret = 0;
5361out:
5362 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005363
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005364#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5365out_ip6_prohibit_entry:
5366 kfree(net->ipv6.ip6_prohibit_entry);
5367out_ip6_null_entry:
5368 kfree(net->ipv6.ip6_null_entry);
5369#endif
David Ahern421842e2018-04-17 17:33:18 -07005370out_fib6_null_entry:
5371 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005372out_ip6_dst_entries:
5373 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005374out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005375 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005376}
5377
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005378static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005379{
David Ahern421842e2018-04-17 17:33:18 -07005380 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005381 kfree(net->ipv6.ip6_null_entry);
5382#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5383 kfree(net->ipv6.ip6_prohibit_entry);
5384 kfree(net->ipv6.ip6_blk_hole_entry);
5385#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005386 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005387}
5388
Thomas Grafd1896342012-06-18 12:08:33 +00005389static int __net_init ip6_route_net_init_late(struct net *net)
5390{
5391#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005392 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5393 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005394 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5395 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005396#endif
5397 return 0;
5398}
5399
5400static void __net_exit ip6_route_net_exit_late(struct net *net)
5401{
5402#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005403 remove_proc_entry("ipv6_route", net->proc_net);
5404 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005405#endif
5406}
5407
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005408static struct pernet_operations ip6_route_net_ops = {
5409 .init = ip6_route_net_init,
5410 .exit = ip6_route_net_exit,
5411};
5412
David S. Millerc3426b42012-06-09 16:27:05 -07005413static int __net_init ipv6_inetpeer_init(struct net *net)
5414{
5415 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5416
5417 if (!bp)
5418 return -ENOMEM;
5419 inet_peer_base_init(bp);
5420 net->ipv6.peers = bp;
5421 return 0;
5422}
5423
5424static void __net_exit ipv6_inetpeer_exit(struct net *net)
5425{
5426 struct inet_peer_base *bp = net->ipv6.peers;
5427
5428 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005429 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005430 kfree(bp);
5431}
5432
David S. Miller2b823f72012-06-09 19:00:16 -07005433static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005434 .init = ipv6_inetpeer_init,
5435 .exit = ipv6_inetpeer_exit,
5436};
5437
Thomas Grafd1896342012-06-18 12:08:33 +00005438static struct pernet_operations ip6_route_net_late_ops = {
5439 .init = ip6_route_net_init_late,
5440 .exit = ip6_route_net_exit_late,
5441};
5442
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005443static struct notifier_block ip6_route_dev_notifier = {
5444 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005445 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005446};
5447
WANG Cong2f460932017-05-03 22:07:31 -07005448void __init ip6_route_init_special_entries(void)
5449{
5450 /* Registering of the loopback is done before this portion of code,
5451 * the loopback reference in rt6_info will not be taken, do it
5452 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005453 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005454 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5455 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5456 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5457 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5458 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5459 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5460 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5461 #endif
5462}
5463
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005464int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005466 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005467 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005468
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005469 ret = -ENOMEM;
5470 ip6_dst_ops_template.kmem_cachep =
5471 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5472 SLAB_HWCACHE_ALIGN, NULL);
5473 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005474 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005475
Eric Dumazetfc66f952010-10-08 06:37:34 +00005476 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005477 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005478 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005479
David S. Millerc3426b42012-06-09 16:27:05 -07005480 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5481 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005482 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005483
David S. Miller7e52b332012-06-15 15:51:55 -07005484 ret = register_pernet_subsys(&ip6_route_net_ops);
5485 if (ret)
5486 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005487
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005488 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5489
David S. Millere8803b62012-06-16 01:12:19 -07005490 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005491 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005492 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005493
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005494 ret = xfrm6_init();
5495 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005496 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005497
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005498 ret = fib6_rules_init();
5499 if (ret)
5500 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005501
Thomas Grafd1896342012-06-18 12:08:33 +00005502 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5503 if (ret)
5504 goto fib6_rules_init;
5505
Florian Westphal16feebc2017-12-02 21:44:08 +01005506 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5507 inet6_rtm_newroute, NULL, 0);
5508 if (ret < 0)
5509 goto out_register_late_subsys;
5510
5511 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5512 inet6_rtm_delroute, NULL, 0);
5513 if (ret < 0)
5514 goto out_register_late_subsys;
5515
5516 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5517 inet6_rtm_getroute, NULL,
5518 RTNL_FLAG_DOIT_UNLOCKED);
5519 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005520 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005521
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005522 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005523 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005524 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005525
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005526 for_each_possible_cpu(cpu) {
5527 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5528
5529 INIT_LIST_HEAD(&ul->head);
5530 spin_lock_init(&ul->lock);
5531 }
5532
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005533out:
5534 return ret;
5535
Thomas Grafd1896342012-06-18 12:08:33 +00005536out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005537 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005538 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005539fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005540 fib6_rules_cleanup();
5541xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005542 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005543out_fib6_init:
5544 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005545out_register_subsys:
5546 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005547out_register_inetpeer:
5548 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005549out_dst_entries:
5550 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005551out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005552 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005553 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005554}
5555
5556void ip6_route_cleanup(void)
5557{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005558 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005559 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005560 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005561 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005562 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005563 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005564 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005565 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005566 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005567}