blob: 80a23da08f65e4feed9578276e526fc955f397cf [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 Ahern8d1c8022018-04-17 17:33:26 -0700494static inline struct fib6_info *rt6_device_match(struct net *net,
495 struct fib6_info *rt,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000496 const struct in6_addr *saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 int oif,
YOSHIFUJI Hideakid4208952008-06-27 20:14:54 -0700498 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
David Ahern0c59d002019-04-09 14:41:18 -0700500 const struct fib6_nh *nh;
David Ahern8d1c8022018-04-17 17:33:26 -0700501 struct fib6_info *sprt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
David Ahern5e670d82018-04-17 17:33:14 -0700503 if (!oif && ipv6_addr_any(saddr) &&
David Ahernad1601a2019-03-27 20:53:56 -0700504 !(rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD))
Ido Schimmel8067bb82018-01-07 12:45:09 +0200505 return rt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900506
David Ahern8fb11a92018-05-04 13:54:24 -0700507 for (sprt = rt; sprt; sprt = rcu_dereference(sprt->fib6_next)) {
David Ahern0c59d002019-04-09 14:41:18 -0700508 nh = &sprt->fib6_nh;
509 if (__rt6_device_match(net, nh, saddr, oif, flags))
510 return sprt;
YOSHIFUJI Hideakidd3abc42008-07-02 18:30:18 +0900511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
David Aherneea68cd2018-04-18 15:39:02 -0700513 if (oif && flags & RT6_LOOKUP_F_IFACE)
514 return net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
David Ahernad1601a2019-03-27 20:53:56 -0700516 return rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800519#ifdef CONFIG_IPV6_ROUTER_PREF
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200520struct __rt6_probe_work {
521 struct work_struct work;
522 struct in6_addr target;
523 struct net_device *dev;
524};
525
526static void rt6_probe_deferred(struct work_struct *w)
527{
528 struct in6_addr mcaddr;
529 struct __rt6_probe_work *work =
530 container_of(w, struct __rt6_probe_work, work);
531
532 addrconf_addr_solict_mult(&work->target, &mcaddr);
Erik Nordmarkadc176c2016-12-02 14:00:08 -0800533 ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200534 dev_put(work->dev);
Michael Büsch662f5532015-02-08 10:14:07 +0100535 kfree(work);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200536}
537
David Aherncc3a86c2019-04-09 14:41:12 -0700538static void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800539{
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200540 struct __rt6_probe_work *work = NULL;
David Ahern5e670d82018-04-17 17:33:14 -0700541 const struct in6_addr *nh_gw;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000542 struct neighbour *neigh;
David Ahern5e670d82018-04-17 17:33:14 -0700543 struct net_device *dev;
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200544 struct inet6_dev *idev;
David Ahern5e670d82018-04-17 17:33:14 -0700545
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800546 /*
547 * Okay, this does not seem to be appropriate
548 * for now, however, we need to check if it
549 * is really so; aka Router Reachability Probing.
550 *
551 * Router Reachability Probe MUST be rate-limited
552 * to no more than one per minute.
553 */
David Aherncc3a86c2019-04-09 14:41:12 -0700554 if (fib6_nh->fib_nh_gw_family)
Amerigo Wangfdd66812012-09-10 02:48:44 +0000555 return;
David Ahern5e670d82018-04-17 17:33:14 -0700556
David Aherncc3a86c2019-04-09 14:41:12 -0700557 nh_gw = &fib6_nh->fib_nh_gw6;
558 dev = fib6_nh->fib_nh_dev;
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000559 rcu_read_lock_bh();
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200560 idev = __in6_dev_get(dev);
David Ahern5e670d82018-04-17 17:33:14 -0700561 neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000562 if (neigh) {
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700563 if (neigh->nud_state & NUD_VALID)
564 goto out;
565
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000566 write_lock(&neigh->lock);
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700567 if (!(neigh->nud_state & NUD_VALID) &&
568 time_after(jiffies,
David Aherndcd1f572018-04-18 15:39:05 -0700569 neigh->updated + idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700570 work = kmalloc(sizeof(*work), GFP_ATOMIC);
571 if (work)
572 __neigh_set_probe_once(neigh);
Hannes Frederic Sowac2f17e82013-10-21 06:17:15 +0200573 }
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000574 write_unlock(&neigh->lock);
David Aherncc3a86c2019-04-09 14:41:12 -0700575 } else if (time_after(jiffies, fib6_nh->last_probe +
Sabrina Dubrocaf547fac2018-10-12 16:22:47 +0200576 idev->cnf.rtr_probe_interval)) {
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700577 work = kmalloc(sizeof(*work), GFP_ATOMIC);
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000578 }
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700579
580 if (work) {
David Aherncc3a86c2019-04-09 14:41:12 -0700581 fib6_nh->last_probe = jiffies;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700582 INIT_WORK(&work->work, rt6_probe_deferred);
David Ahern5e670d82018-04-17 17:33:14 -0700583 work->target = *nh_gw;
584 dev_hold(dev);
585 work->dev = dev;
Martin KaFai Lau990edb42015-07-24 09:57:42 -0700586 schedule_work(&work->work);
587 }
588
Martin KaFai Lau8d6c31b2015-07-24 09:57:43 -0700589out:
YOSHIFUJI Hideaki / 吉藤英明2152cae2013-01-17 12:53:43 +0000590 rcu_read_unlock_bh();
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800591}
592#else
David Aherncc3a86c2019-04-09 14:41:12 -0700593static inline void rt6_probe(struct fib6_nh *fib6_nh)
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800594{
YOSHIFUJI Hideaki27097252006-03-20 17:05:13 -0800595}
596#endif
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598/*
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800599 * Default Router Selection (RFC 2461 6.3.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 */
David Ahern1ba9a892019-04-09 14:41:10 -0700601static enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200603 enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
David Ahern5e670d82018-04-17 17:33:14 -0700604 struct neighbour *neigh;
Eric Dumazetf2c31e32011-07-29 19:00:53 +0000605
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000606 rcu_read_lock_bh();
David Ahern1ba9a892019-04-09 14:41:10 -0700607 neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev,
608 &fib6_nh->fib_nh_gw6);
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000609 if (neigh) {
610 read_lock(&neigh->lock);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800611 if (neigh->nud_state & NUD_VALID)
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200612 ret = RT6_NUD_SUCCEED;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800613#ifdef CONFIG_IPV6_ROUTER_PREF
Paul Marksa5a81f02012-12-03 10:26:54 +0000614 else if (!(neigh->nud_state & NUD_FAILED))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200615 ret = RT6_NUD_SUCCEED;
Jiri Benc7e980562013-12-11 13:48:20 +0100616 else
617 ret = RT6_NUD_FAIL_PROBE;
YOSHIFUJI Hideaki398bcbe2008-01-19 00:35:16 -0800618#endif
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000619 read_unlock(&neigh->lock);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200620 } else {
621 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
Jiri Benc7e980562013-12-11 13:48:20 +0100622 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
Paul Marksa5a81f02012-12-03 10:26:54 +0000623 }
YOSHIFUJI Hideaki / 吉藤英明145a3622013-01-17 12:53:38 +0000624 rcu_read_unlock_bh();
625
Paul Marksa5a81f02012-12-03 10:26:54 +0000626 return ret;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800627}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
David Ahern702cea52019-04-09 14:41:13 -0700629static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif,
630 int strict)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800631{
David Ahern6e1809a2019-04-09 14:41:11 -0700632 int m = 0;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900633
David Ahern6e1809a2019-04-09 14:41:11 -0700634 if (!oif || nh->fib_nh_dev->ifindex == oif)
635 m = 2;
636
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -0700637 if (!m && (strict & RT6_LOOKUP_F_IFACE))
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200638 return RT6_NUD_FAIL_HARD;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800639#ifdef CONFIG_IPV6_ROUTER_PREF
David Ahern702cea52019-04-09 14:41:13 -0700640 m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -0800641#endif
David Ahern1ba9a892019-04-09 14:41:10 -0700642 if ((strict & RT6_LOOKUP_F_REACHABLE) &&
David Ahern702cea52019-04-09 14:41:13 -0700643 !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) {
David Ahern1ba9a892019-04-09 14:41:10 -0700644 int n = rt6_check_neigh(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200645 if (n < 0)
646 return n;
647 }
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800648 return m;
649}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
David Ahern28679ed2019-04-09 14:41:14 -0700651static bool find_match(struct fib6_nh *nh, u32 fib6_flags,
652 int oif, int strict, int *mpri, bool *do_rr)
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800653{
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200654 bool match_do_rr = false;
David Ahern28679ed2019-04-09 14:41:14 -0700655 bool rc = false;
656 int m;
Andy Gospodarek35103d12015-08-13 10:39:01 -0400657
David Ahern28679ed2019-04-09 14:41:14 -0700658 if (nh->fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel8067bb82018-01-07 12:45:09 +0200659 goto out;
660
David Ahern28679ed2019-04-09 14:41:14 -0700661 if (ip6_ignore_linkdown(nh->fib_nh_dev) &&
662 nh->fib_nh_flags & RTNH_F_LINKDOWN &&
David Ahernd5d32e42016-10-24 12:27:23 -0700663 !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
Andy Gospodarek35103d12015-08-13 10:39:01 -0400664 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700665
David Ahern28679ed2019-04-09 14:41:14 -0700666 m = rt6_score_route(nh, fib6_flags, oif, strict);
Jiri Benc7e980562013-12-11 13:48:20 +0100667 if (m == RT6_NUD_FAIL_DO_RR) {
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200668 match_do_rr = true;
669 m = 0; /* lowest valid score */
Jiri Benc7e980562013-12-11 13:48:20 +0100670 } else if (m == RT6_NUD_FAIL_HARD) {
David S. Millerf11e6652007-03-24 20:36:25 -0700671 goto out;
David S. Millerf11e6652007-03-24 20:36:25 -0700672 }
673
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200674 if (strict & RT6_LOOKUP_F_REACHABLE)
David Ahern28679ed2019-04-09 14:41:14 -0700675 rt6_probe(nh);
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200676
Jiri Benc7e980562013-12-11 13:48:20 +0100677 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200678 if (m > *mpri) {
679 *do_rr = match_do_rr;
680 *mpri = m;
David Ahern28679ed2019-04-09 14:41:14 -0700681 rc = true;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200682 }
David S. Millerf11e6652007-03-24 20:36:25 -0700683out:
David Ahern28679ed2019-04-09 14:41:14 -0700684 return rc;
David S. Millerf11e6652007-03-24 20:36:25 -0700685}
686
David Ahern30c15f02019-04-09 14:41:15 -0700687static void __find_rr_leaf(struct fib6_info *rt_start,
688 struct fib6_info *nomatch, u32 metric,
689 struct fib6_info **match, struct fib6_info **cont,
690 int oif, int strict, bool *do_rr, int *mpri)
David S. Millerf11e6652007-03-24 20:36:25 -0700691{
David Ahern30c15f02019-04-09 14:41:15 -0700692 struct fib6_info *rt;
693
694 for (rt = rt_start;
695 rt && rt != nomatch;
696 rt = rcu_dereference(rt->fib6_next)) {
697 struct fib6_nh *nh;
698
699 if (cont && rt->fib6_metric != metric) {
700 *cont = rt;
701 return;
702 }
703
704 if (fib6_check_expired(rt))
705 continue;
706
707 nh = &rt->fib6_nh;
708 if (find_match(nh, rt->fib6_flags, oif, strict, mpri, do_rr))
709 *match = rt;
710 }
711}
712
713static struct fib6_info *find_rr_leaf(struct fib6_node *fn,
714 struct fib6_info *leaf,
715 struct fib6_info *rr_head,
716 u32 metric, int oif, int strict,
717 bool *do_rr)
718{
719 struct fib6_info *match = NULL, *cont = NULL;
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800720 int mpri = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
David Ahern30c15f02019-04-09 14:41:15 -0700722 __find_rr_leaf(rr_head, NULL, metric, &match, &cont,
723 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700724
David Ahern30c15f02019-04-09 14:41:15 -0700725 __find_rr_leaf(leaf, rr_head, metric, &match, &cont,
726 oif, strict, do_rr, &mpri);
Steffen Klassert9fbdcfa2015-04-28 13:03:04 -0700727
728 if (match || !cont)
729 return match;
730
David Ahern30c15f02019-04-09 14:41:15 -0700731 __find_rr_leaf(cont, NULL, metric, &match, NULL,
732 oif, strict, do_rr, &mpri);
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800733
David S. Millerf11e6652007-03-24 20:36:25 -0700734 return match;
735}
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800736
David Ahern8d1c8022018-04-17 17:33:26 -0700737static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn,
Wei Wang8d1040e2017-10-06 12:06:08 -0700738 int oif, int strict)
David S. Millerf11e6652007-03-24 20:36:25 -0700739{
David Ahern8d1c8022018-04-17 17:33:26 -0700740 struct fib6_info *leaf = rcu_dereference(fn->leaf);
741 struct fib6_info *match, *rt0;
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200742 bool do_rr = false;
Wei Wang17ecf592017-10-06 12:06:09 -0700743 int key_plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
David Ahern421842e2018-04-17 17:33:18 -0700745 if (!leaf || leaf == net->ipv6.fib6_null_entry)
746 return net->ipv6.fib6_null_entry;
Wei Wang8d1040e2017-10-06 12:06:08 -0700747
Wei Wang66f5d6c2017-10-06 12:06:10 -0700748 rt0 = rcu_dereference(fn->rr_ptr);
David S. Millerf11e6652007-03-24 20:36:25 -0700749 if (!rt0)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700750 rt0 = leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Wei Wang17ecf592017-10-06 12:06:09 -0700752 /* Double check to make sure fn is not an intermediate node
753 * and fn->leaf does not points to its child's leaf
754 * (This might happen if all routes under fn are deleted from
755 * the tree and fib6_repair_tree() is called on the node.)
756 */
David Ahern93c2fb22018-04-18 15:38:59 -0700757 key_plen = rt0->fib6_dst.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700758#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -0700759 if (rt0->fib6_src.plen)
760 key_plen = rt0->fib6_src.plen;
Wei Wang17ecf592017-10-06 12:06:09 -0700761#endif
762 if (fn->fn_bit != key_plen)
David Ahern421842e2018-04-17 17:33:18 -0700763 return net->ipv6.fib6_null_entry;
Wei Wang17ecf592017-10-06 12:06:09 -0700764
David Ahern93c2fb22018-04-18 15:38:59 -0700765 match = find_rr_leaf(fn, leaf, rt0, rt0->fib6_metric, oif, strict,
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200766 &do_rr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Hannes Frederic Sowaafc154e2013-07-11 12:43:42 +0200768 if (do_rr) {
David Ahern8fb11a92018-05-04 13:54:24 -0700769 struct fib6_info *next = rcu_dereference(rt0->fib6_next);
David S. Millerf11e6652007-03-24 20:36:25 -0700770
YOSHIFUJI Hideaki554cfb72006-03-20 17:00:26 -0800771 /* no entries matched; do round-robin */
David Ahern93c2fb22018-04-18 15:38:59 -0700772 if (!next || next->fib6_metric != rt0->fib6_metric)
Wei Wang8d1040e2017-10-06 12:06:08 -0700773 next = leaf;
David S. Millerf11e6652007-03-24 20:36:25 -0700774
Wei Wang66f5d6c2017-10-06 12:06:10 -0700775 if (next != rt0) {
David Ahern93c2fb22018-04-18 15:38:59 -0700776 spin_lock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700777 /* make sure next is not being deleted from the tree */
David Ahern93c2fb22018-04-18 15:38:59 -0700778 if (next->fib6_node)
Wei Wang66f5d6c2017-10-06 12:06:10 -0700779 rcu_assign_pointer(fn->rr_ptr, next);
David Ahern93c2fb22018-04-18 15:38:59 -0700780 spin_unlock_bh(&leaf->fib6_table->tb6_lock);
Wei Wang66f5d6c2017-10-06 12:06:10 -0700781 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 }
783
David Ahern421842e2018-04-17 17:33:18 -0700784 return match ? match : net->ipv6.fib6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785}
786
David Ahern85bd05d2019-04-16 14:36:01 -0700787static bool rt6_is_gw_or_nonexthop(const struct fib6_result *res)
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700788{
David Ahern85bd05d2019-04-16 14:36:01 -0700789 return (res->f6i->fib6_flags & RTF_NONEXTHOP) ||
790 res->nh->fib_nh_gw_family;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -0700791}
792
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800793#ifdef CONFIG_IPV6_ROUTE_INFO
794int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000795 const struct in6_addr *gwaddr)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800796{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900797 struct net *net = dev_net(dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800798 struct route_info *rinfo = (struct route_info *) opt;
799 struct in6_addr prefix_buf, *prefix;
800 unsigned int pref;
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900801 unsigned long lifetime;
David Ahern8d1c8022018-04-17 17:33:26 -0700802 struct fib6_info *rt;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800803
804 if (len < sizeof(struct route_info)) {
805 return -EINVAL;
806 }
807
808 /* Sanity check for prefix_len and length */
809 if (rinfo->length > 3) {
810 return -EINVAL;
811 } else if (rinfo->prefix_len > 128) {
812 return -EINVAL;
813 } else if (rinfo->prefix_len > 64) {
814 if (rinfo->length < 2) {
815 return -EINVAL;
816 }
817 } else if (rinfo->prefix_len > 0) {
818 if (rinfo->length < 1) {
819 return -EINVAL;
820 }
821 }
822
823 pref = rinfo->route_pref;
824 if (pref == ICMPV6_ROUTER_PREF_INVALID)
Jens Rosenboom3933fc92009-09-10 06:25:11 +0000825 return -EINVAL;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800826
YOSHIFUJI Hideaki4bed72e2008-05-27 17:37:49 +0900827 lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800828
829 if (rinfo->length == 3)
830 prefix = (struct in6_addr *)rinfo->prefix;
831 else {
832 /* this function is safe */
833 ipv6_addr_prefix(&prefix_buf,
834 (struct in6_addr *)rinfo->prefix,
835 rinfo->prefix_len);
836 prefix = &prefix_buf;
837 }
838
Duan Jiongf104a562013-11-08 09:56:53 +0800839 if (rinfo->prefix_len == 0)
David Ahernafb1d4b52018-04-17 17:33:11 -0700840 rt = rt6_get_dflt_router(net, gwaddr, dev);
Duan Jiongf104a562013-11-08 09:56:53 +0800841 else
842 rt = rt6_get_route_info(net, prefix, rinfo->prefix_len,
David Ahern830218c2016-10-24 10:52:35 -0700843 gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800844
845 if (rt && !lifetime) {
David Ahernafb1d4b52018-04-17 17:33:11 -0700846 ip6_del_rt(net, rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800847 rt = NULL;
848 }
849
850 if (!rt && lifetime)
David Ahern830218c2016-10-24 10:52:35 -0700851 rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr,
852 dev, pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800853 else if (rt)
David Ahern93c2fb22018-04-18 15:38:59 -0700854 rt->fib6_flags = RTF_ROUTEINFO |
855 (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800856
857 if (rt) {
Gao feng1716a962012-04-06 00:13:10 +0000858 if (!addrconf_finite_timeout(lifetime))
David Ahern14895682018-04-17 17:33:17 -0700859 fib6_clean_expires(rt);
Gao feng1716a962012-04-06 00:13:10 +0000860 else
David Ahern14895682018-04-17 17:33:17 -0700861 fib6_set_expires(rt, jiffies + HZ * lifetime);
Gao feng1716a962012-04-06 00:13:10 +0000862
David Ahern93531c62018-04-17 17:33:25 -0700863 fib6_info_release(rt);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800864 }
865 return 0;
866}
867#endif
868
David Ahernae90d862018-04-17 17:33:12 -0700869/*
870 * Misc support functions
871 */
872
873/* called with rcu_lock held */
David Ahern0d161582019-04-16 14:36:04 -0700874static struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -0700875{
David Ahern0d161582019-04-16 14:36:04 -0700876 struct net_device *dev = res->nh->fib_nh_dev;
877 const struct fib6_info *f6i = res->f6i;
David Ahernae90d862018-04-17 17:33:12 -0700878
David Ahern0d161582019-04-16 14:36:04 -0700879 if (f6i->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) {
David Ahernae90d862018-04-17 17:33:12 -0700880 /* for copies of local routes, dst->dev needs to be the
881 * device if it is a master device, the master device if
882 * device is enslaved, and the loopback as the default
883 */
884 if (netif_is_l3_slave(dev) &&
David Ahern0d161582019-04-16 14:36:04 -0700885 !rt6_need_strict(&f6i->fib6_dst.addr))
David Ahernae90d862018-04-17 17:33:12 -0700886 dev = l3mdev_master_dev_rcu(dev);
887 else if (!netif_is_l3_master(dev))
888 dev = dev_net(dev)->loopback_dev;
889 /* last case is netif_is_l3_master(dev) is true in which
890 * case we want dev returned to be dev
891 */
892 }
893
894 return dev;
895}
896
David Ahern6edb3c92018-04-17 17:33:15 -0700897static const int fib6_prop[RTN_MAX + 1] = {
898 [RTN_UNSPEC] = 0,
899 [RTN_UNICAST] = 0,
900 [RTN_LOCAL] = 0,
901 [RTN_BROADCAST] = 0,
902 [RTN_ANYCAST] = 0,
903 [RTN_MULTICAST] = 0,
904 [RTN_BLACKHOLE] = -EINVAL,
905 [RTN_UNREACHABLE] = -EHOSTUNREACH,
906 [RTN_PROHIBIT] = -EACCES,
907 [RTN_THROW] = -EAGAIN,
908 [RTN_NAT] = -EINVAL,
909 [RTN_XRESOLVE] = -EINVAL,
910};
911
912static int ip6_rt_type_to_error(u8 fib6_type)
913{
914 return fib6_prop[fib6_type];
915}
916
David Ahern8d1c8022018-04-17 17:33:26 -0700917static unsigned short fib6_info_dst_flags(struct fib6_info *rt)
David Ahern3b6761d2018-04-17 17:33:20 -0700918{
919 unsigned short flags = 0;
920
921 if (rt->dst_nocount)
922 flags |= DST_NOCOUNT;
923 if (rt->dst_nopolicy)
924 flags |= DST_NOPOLICY;
925 if (rt->dst_host)
926 flags |= DST_HOST;
927
928 return flags;
929}
930
David Ahern8d1c8022018-04-17 17:33:26 -0700931static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort)
David Ahern6edb3c92018-04-17 17:33:15 -0700932{
933 rt->dst.error = ip6_rt_type_to_error(ort->fib6_type);
934
935 switch (ort->fib6_type) {
936 case RTN_BLACKHOLE:
937 rt->dst.output = dst_discard_out;
938 rt->dst.input = dst_discard;
939 break;
940 case RTN_PROHIBIT:
941 rt->dst.output = ip6_pkt_prohibit_out;
942 rt->dst.input = ip6_pkt_prohibit;
943 break;
944 case RTN_THROW:
945 case RTN_UNREACHABLE:
946 default:
947 rt->dst.output = ip6_pkt_discard_out;
948 rt->dst.input = ip6_pkt_discard;
949 break;
950 }
951}
952
David Ahern0d161582019-04-16 14:36:04 -0700953static void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res)
David Ahern6edb3c92018-04-17 17:33:15 -0700954{
David Ahern0d161582019-04-16 14:36:04 -0700955 struct fib6_info *ort = res->f6i;
956
David Ahern93c2fb22018-04-18 15:38:59 -0700957 if (ort->fib6_flags & RTF_REJECT) {
David Ahern6edb3c92018-04-17 17:33:15 -0700958 ip6_rt_init_dst_reject(rt, ort);
959 return;
960 }
961
962 rt->dst.error = 0;
963 rt->dst.output = ip6_output;
964
Hangbin Liud23c4b62018-08-23 11:31:37 +0800965 if (ort->fib6_type == RTN_LOCAL || ort->fib6_type == RTN_ANYCAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700966 rt->dst.input = ip6_input;
David Ahern93c2fb22018-04-18 15:38:59 -0700967 } else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) {
David Ahern6edb3c92018-04-17 17:33:15 -0700968 rt->dst.input = ip6_mc_input;
969 } else {
970 rt->dst.input = ip6_forward;
971 }
972
David Ahern0d161582019-04-16 14:36:04 -0700973 if (res->nh->fib_nh_lws) {
974 rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws);
David Ahern6edb3c92018-04-17 17:33:15 -0700975 lwtunnel_set_redirect(&rt->dst);
976 }
977
978 rt->dst.lastuse = jiffies;
979}
980
Wei Wange873e4b2018-07-21 20:56:32 -0700981/* Caller must already hold reference to @from */
David Ahern8d1c8022018-04-17 17:33:26 -0700982static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
David Ahernae90d862018-04-17 17:33:12 -0700983{
David Ahernae90d862018-04-17 17:33:12 -0700984 rt->rt6i_flags &= ~RTF_EXPIRES;
David Aherna68886a2018-04-20 15:38:02 -0700985 rcu_assign_pointer(rt->from, from);
David Aherne1255ed2018-10-04 20:07:53 -0700986 ip_dst_init_metrics(&rt->dst, from->fib6_metrics);
David Ahernae90d862018-04-17 17:33:12 -0700987}
988
David Ahern0d161582019-04-16 14:36:04 -0700989/* Caller must already hold reference to f6i in result */
990static void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res)
David Ahernae90d862018-04-17 17:33:12 -0700991{
David Ahern0d161582019-04-16 14:36:04 -0700992 const struct fib6_nh *nh = res->nh;
993 const struct net_device *dev = nh->fib_nh_dev;
994 struct fib6_info *f6i = res->f6i;
David Aherndcd1f572018-04-18 15:39:05 -0700995
David Ahern0d161582019-04-16 14:36:04 -0700996 ip6_rt_init_dst(rt, res);
David Ahern6edb3c92018-04-17 17:33:15 -0700997
David Ahern0d161582019-04-16 14:36:04 -0700998 rt->rt6i_dst = f6i->fib6_dst;
David Aherndcd1f572018-04-18 15:39:05 -0700999 rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL;
David Ahern0d161582019-04-16 14:36:04 -07001000 rt->rt6i_flags = f6i->fib6_flags;
1001 if (nh->fib_nh_gw_family) {
1002 rt->rt6i_gateway = nh->fib_nh_gw6;
David Ahern2b2450c2019-03-27 20:53:52 -07001003 rt->rt6i_flags |= RTF_GATEWAY;
1004 }
David Ahern0d161582019-04-16 14:36:04 -07001005 rt6_set_from(rt, f6i);
David Ahernae90d862018-04-17 17:33:12 -07001006#ifdef CONFIG_IPV6_SUBTREES
David Ahern0d161582019-04-16 14:36:04 -07001007 rt->rt6i_src = f6i->fib6_src;
David Ahernae90d862018-04-17 17:33:12 -07001008#endif
David Ahernae90d862018-04-17 17:33:12 -07001009}
1010
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001011static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
1012 struct in6_addr *saddr)
1013{
Wei Wang66f5d6c2017-10-06 12:06:10 -07001014 struct fib6_node *pn, *sn;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001015 while (1) {
1016 if (fn->fn_flags & RTN_TL_ROOT)
1017 return NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001018 pn = rcu_dereference(fn->parent);
1019 sn = FIB6_SUBTREE(pn);
1020 if (sn && sn != fn)
David Ahern64547432018-05-09 20:34:19 -07001021 fn = fib6_node_lookup(sn, NULL, saddr);
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001022 else
1023 fn = pn;
1024 if (fn->fn_flags & RTN_RTINFO)
1025 return fn;
1026 }
1027}
Thomas Grafc71099a2006-08-04 23:20:06 -07001028
David Ahern10585b42019-03-20 09:24:50 -07001029static bool ip6_hold_safe(struct net *net, struct rt6_info **prt)
Wei Wangd3843fe2017-10-06 12:06:06 -07001030{
1031 struct rt6_info *rt = *prt;
1032
1033 if (dst_hold_safe(&rt->dst))
1034 return true;
David Ahern10585b42019-03-20 09:24:50 -07001035 if (net) {
Wei Wangd3843fe2017-10-06 12:06:06 -07001036 rt = net->ipv6.ip6_null_entry;
1037 dst_hold(&rt->dst);
1038 } else {
1039 rt = NULL;
1040 }
1041 *prt = rt;
1042 return false;
1043}
1044
David Aherndec9b0e2018-04-17 17:33:19 -07001045/* called with rcu_lock held */
David Ahern9b6b35a2019-04-16 14:36:02 -07001046static struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res)
David Aherndec9b0e2018-04-17 17:33:19 -07001047{
David Ahern9b6b35a2019-04-16 14:36:02 -07001048 struct net_device *dev = res->nh->fib_nh_dev;
1049 struct fib6_info *f6i = res->f6i;
1050 unsigned short flags;
David Aherndec9b0e2018-04-17 17:33:19 -07001051 struct rt6_info *nrt;
1052
David Ahern9b6b35a2019-04-16 14:36:02 -07001053 if (!fib6_info_hold_safe(f6i))
Xin Long1c87e792019-03-20 14:45:48 +08001054 goto fallback;
Wei Wange873e4b2018-07-21 20:56:32 -07001055
David Ahern9b6b35a2019-04-16 14:36:02 -07001056 flags = fib6_info_dst_flags(f6i);
David Ahern93531c62018-04-17 17:33:25 -07001057 nrt = ip6_dst_alloc(dev_net(dev), dev, flags);
Xin Long1c87e792019-03-20 14:45:48 +08001058 if (!nrt) {
David Ahern9b6b35a2019-04-16 14:36:02 -07001059 fib6_info_release(f6i);
Xin Long1c87e792019-03-20 14:45:48 +08001060 goto fallback;
1061 }
David Aherndec9b0e2018-04-17 17:33:19 -07001062
David Ahern0d161582019-04-16 14:36:04 -07001063 ip6_rt_copy_init(nrt, res);
Xin Long1c87e792019-03-20 14:45:48 +08001064 return nrt;
1065
1066fallback:
1067 nrt = dev_net(dev)->ipv6.ip6_null_entry;
1068 dst_hold(&nrt->dst);
David Aherndec9b0e2018-04-17 17:33:19 -07001069 return nrt;
1070}
1071
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001072static struct rt6_info *ip6_pol_route_lookup(struct net *net,
1073 struct fib6_table *table,
David Ahernb75cc8f2018-03-02 08:32:17 -08001074 struct flowi6 *fl6,
1075 const struct sk_buff *skb,
1076 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077{
David Ahernb1d40992019-04-16 14:35:59 -07001078 struct fib6_result res = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 struct fib6_node *fn;
David Ahern23fb93a2018-04-17 17:33:23 -07001080 struct rt6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
David Ahernb6cdbc82018-03-29 17:44:57 -07001082 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1083 flags &= ~RT6_LOOKUP_F_IFACE;
1084
Wei Wang66f5d6c2017-10-06 12:06:10 -07001085 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07001086 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Thomas Grafc71099a2006-08-04 23:20:06 -07001087restart:
David Ahernb1d40992019-04-16 14:35:59 -07001088 res.f6i = rcu_dereference(fn->leaf);
1089 if (!res.f6i)
1090 res.f6i = net->ipv6.fib6_null_entry;
David Ahernaf52a522019-04-09 14:41:16 -07001091 else
David Ahernb1d40992019-04-16 14:35:59 -07001092 res.f6i = rt6_device_match(net, res.f6i, &fl6->saddr,
1093 fl6->flowi6_oif, flags);
David Ahernaf52a522019-04-09 14:41:16 -07001094
David Ahernb1d40992019-04-16 14:35:59 -07001095 if (res.f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001096 fn = fib6_backtrack(fn, &fl6->saddr);
1097 if (fn)
1098 goto restart;
David Ahernaf52a522019-04-09 14:41:16 -07001099
1100 rt = net->ipv6.ip6_null_entry;
1101 dst_hold(&rt->dst);
1102 goto out;
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001103 }
Wei Wang2b760fc2017-10-06 12:06:03 -07001104
David Ahernb1d40992019-04-16 14:35:59 -07001105 fib6_select_path(net, &res, fl6, fl6->flowi6_oif,
1106 fl6->flowi6_oif != 0, skb, flags);
1107
David S. Miller4c9483b2011-03-12 16:22:43 -05001108 /* Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001109 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001110 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001111 if (ip6_hold_safe(net, &rt))
David Aherndec9b0e2018-04-17 17:33:19 -07001112 dst_use_noref(&rt->dst, jiffies);
David Ahern23fb93a2018-04-17 17:33:23 -07001113 } else {
David Ahern9b6b35a2019-04-16 14:36:02 -07001114 rt = ip6_create_rt_rcu(&res);
David Aherndec9b0e2018-04-17 17:33:19 -07001115 }
Wei Wangd3843fe2017-10-06 12:06:06 -07001116
David Ahernaf52a522019-04-09 14:41:16 -07001117out:
David Ahernb1d40992019-04-16 14:35:59 -07001118 trace_fib6_table_lookup(net, res.f6i, table, fl6);
David Ahernaf52a522019-04-09 14:41:16 -07001119
Wei Wang66f5d6c2017-10-06 12:06:10 -07001120 rcu_read_unlock();
David Ahernb8115802015-11-19 12:24:22 -08001121
Thomas Grafc71099a2006-08-04 23:20:06 -07001122 return rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07001123}
1124
Ian Morris67ba4152014-08-24 21:53:10 +01001125struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08001126 const struct sk_buff *skb, int flags)
Florian Westphalea6e5742011-09-05 16:05:44 +02001127{
David Ahernb75cc8f2018-03-02 08:32:17 -08001128 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup);
Florian Westphalea6e5742011-09-05 16:05:44 +02001129}
1130EXPORT_SYMBOL_GPL(ip6_route_lookup);
1131
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001132struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
David Ahernb75cc8f2018-03-02 08:32:17 -08001133 const struct in6_addr *saddr, int oif,
1134 const struct sk_buff *skb, int strict)
Thomas Grafc71099a2006-08-04 23:20:06 -07001135{
David S. Miller4c9483b2011-03-12 16:22:43 -05001136 struct flowi6 fl6 = {
1137 .flowi6_oif = oif,
1138 .daddr = *daddr,
Thomas Grafc71099a2006-08-04 23:20:06 -07001139 };
1140 struct dst_entry *dst;
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07001141 int flags = strict ? RT6_LOOKUP_F_IFACE : 0;
Thomas Grafc71099a2006-08-04 23:20:06 -07001142
Thomas Grafadaa70b2006-10-13 15:01:03 -07001143 if (saddr) {
David S. Miller4c9483b2011-03-12 16:22:43 -05001144 memcpy(&fl6.saddr, saddr, sizeof(*saddr));
Thomas Grafadaa70b2006-10-13 15:01:03 -07001145 flags |= RT6_LOOKUP_F_HAS_SADDR;
1146 }
1147
David Ahernb75cc8f2018-03-02 08:32:17 -08001148 dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup);
Thomas Grafc71099a2006-08-04 23:20:06 -07001149 if (dst->error == 0)
1150 return (struct rt6_info *) dst;
1151
1152 dst_release(dst);
1153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 return NULL;
1155}
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +09001156EXPORT_SYMBOL(rt6_lookup);
1157
Thomas Grafc71099a2006-08-04 23:20:06 -07001158/* ip6_ins_rt is called with FREE table->tb6_lock.
Wei Wang1cfb71e2017-06-17 10:42:33 -07001159 * It takes new route entry, the addition fails by any reason the
1160 * route is released.
1161 * Caller must hold dst before calling it.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 */
1163
David Ahern8d1c8022018-04-17 17:33:26 -07001164static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info,
David Ahern333c4302017-05-21 10:12:04 -06001165 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166{
1167 int err;
Thomas Grafc71099a2006-08-04 23:20:06 -07001168 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
David Ahern93c2fb22018-04-18 15:38:59 -07001170 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001171 spin_lock_bh(&table->tb6_lock);
David Ahernd4ead6b2018-04-17 17:33:16 -07001172 err = fib6_add(&table->tb6_root, rt, info, extack);
Wei Wang66f5d6c2017-10-06 12:06:10 -07001173 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174
1175 return err;
1176}
1177
David Ahern8d1c8022018-04-17 17:33:26 -07001178int ip6_ins_rt(struct net *net, struct fib6_info *rt)
Thomas Graf40e22e82006-08-22 00:00:45 -07001179{
David Ahernafb1d4b52018-04-17 17:33:11 -07001180 struct nl_info info = { .nl_net = net, };
Florian Westphale715b6d2015-01-05 23:57:44 +01001181
David Ahernd4ead6b2018-04-17 17:33:16 -07001182 return __ip6_ins_rt(rt, &info, NULL);
Thomas Graf40e22e82006-08-22 00:00:45 -07001183}
1184
David Ahern85bd05d2019-04-16 14:36:01 -07001185static struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res,
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001186 const struct in6_addr *daddr,
1187 const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188{
David Ahern85bd05d2019-04-16 14:36:01 -07001189 struct fib6_info *f6i = res->f6i;
David Ahern4832c302017-08-17 12:17:20 -07001190 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 struct rt6_info *rt;
1192
1193 /*
1194 * Clone the route.
1195 */
1196
David Ahern85bd05d2019-04-16 14:36:01 -07001197 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001198 return NULL;
1199
David Ahern0d161582019-04-16 14:36:04 -07001200 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001201 rt = ip6_dst_alloc(dev_net(dev), dev, 0);
Wei Wange873e4b2018-07-21 20:56:32 -07001202 if (!rt) {
David Ahern85bd05d2019-04-16 14:36:01 -07001203 fib6_info_release(f6i);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001204 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001205 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001206
David Ahern0d161582019-04-16 14:36:04 -07001207 ip6_rt_copy_init(rt, res);
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001208 rt->rt6i_flags |= RTF_CACHE;
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001209 rt->dst.flags |= DST_HOST;
1210 rt->rt6i_dst.addr = *daddr;
1211 rt->rt6i_dst.plen = 128;
1212
David Ahern85bd05d2019-04-16 14:36:01 -07001213 if (!rt6_is_gw_or_nonexthop(res)) {
1214 if (f6i->fib6_dst.plen != 128 &&
1215 ipv6_addr_equal(&f6i->fib6_dst.addr, daddr))
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001216 rt->rt6i_flags |= RTF_ANYCAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217#ifdef CONFIG_IPV6_SUBTREES
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001218 if (rt->rt6i_src.plen && saddr) {
1219 rt->rt6i_src.addr = *saddr;
1220 rt->rt6i_src.plen = 128;
Martin KaFai Lau8b9df262015-05-22 20:55:59 -07001221 }
Martin KaFai Lau83a09ab2015-05-22 20:56:05 -07001222#endif
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001223 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224
YOSHIFUJI Hideaki95a9a5b2006-03-20 16:55:51 -08001225 return rt;
1226}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
David Aherndb3fede2019-04-16 14:36:03 -07001228static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001229{
David Aherndb3fede2019-04-16 14:36:03 -07001230 struct fib6_info *f6i = res->f6i;
1231 unsigned short flags = fib6_info_dst_flags(f6i);
David Ahern4832c302017-08-17 12:17:20 -07001232 struct net_device *dev;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001233 struct rt6_info *pcpu_rt;
1234
David Aherndb3fede2019-04-16 14:36:03 -07001235 if (!fib6_info_hold_safe(f6i))
Wei Wange873e4b2018-07-21 20:56:32 -07001236 return NULL;
1237
David Ahern4832c302017-08-17 12:17:20 -07001238 rcu_read_lock();
David Ahern0d161582019-04-16 14:36:04 -07001239 dev = ip6_rt_get_dev_rcu(res);
David Ahern93531c62018-04-17 17:33:25 -07001240 pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags);
David Ahern4832c302017-08-17 12:17:20 -07001241 rcu_read_unlock();
Wei Wange873e4b2018-07-21 20:56:32 -07001242 if (!pcpu_rt) {
David Aherndb3fede2019-04-16 14:36:03 -07001243 fib6_info_release(f6i);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001244 return NULL;
Wei Wange873e4b2018-07-21 20:56:32 -07001245 }
David Ahern0d161582019-04-16 14:36:04 -07001246 ip6_rt_copy_init(pcpu_rt, res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001247 pcpu_rt->rt6i_flags |= RTF_PCPU;
1248 return pcpu_rt;
1249}
1250
Wei Wang66f5d6c2017-10-06 12:06:10 -07001251/* It should be called with rcu_read_lock() acquired */
David Aherndb3fede2019-04-16 14:36:03 -07001252static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001253{
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001254 struct rt6_info *pcpu_rt, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001255
David Aherndb3fede2019-04-16 14:36:03 -07001256 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001257 pcpu_rt = *p;
1258
David Ahernd4ead6b2018-04-17 17:33:16 -07001259 if (pcpu_rt)
David Ahern10585b42019-03-20 09:24:50 -07001260 ip6_hold_safe(NULL, &pcpu_rt);
Wei Wangd3843fe2017-10-06 12:06:06 -07001261
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001262 return pcpu_rt;
1263}
1264
David Ahernafb1d4b52018-04-17 17:33:11 -07001265static struct rt6_info *rt6_make_pcpu_route(struct net *net,
David Aherndb3fede2019-04-16 14:36:03 -07001266 const struct fib6_result *res)
Martin KaFai Laua73e4192015-08-14 11:05:53 -07001267{
1268 struct rt6_info *pcpu_rt, *prev, **p;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001269
David Aherndb3fede2019-04-16 14:36:03 -07001270 pcpu_rt = ip6_rt_pcpu_alloc(res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001271 if (!pcpu_rt) {
Martin KaFai Lau9c7370a2015-08-14 11:05:54 -07001272 dst_hold(&net->ipv6.ip6_null_entry->dst);
1273 return net->ipv6.ip6_null_entry;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001274 }
1275
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001276 dst_hold(&pcpu_rt->dst);
David Aherndb3fede2019-04-16 14:36:03 -07001277 p = this_cpu_ptr(res->f6i->rt6i_pcpu);
Wei Wanga94b9362017-10-06 12:06:04 -07001278 prev = cmpxchg(p, NULL, pcpu_rt);
Eric Dumazet951f7882017-10-08 21:07:18 -07001279 BUG_ON(prev);
Wei Wanga94b9362017-10-06 12:06:04 -07001280
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001281 return pcpu_rt;
1282}
1283
Wei Wang35732d02017-10-06 12:05:57 -07001284/* exception hash table implementation
1285 */
1286static DEFINE_SPINLOCK(rt6_exception_lock);
1287
1288/* Remove rt6_ex from hash table and free the memory
1289 * Caller must hold rt6_exception_lock
1290 */
1291static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1292 struct rt6_exception *rt6_ex)
1293{
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001294 struct fib6_info *from;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001295 struct net *net;
Wei Wang81eb8442017-10-06 12:06:11 -07001296
Wei Wang35732d02017-10-06 12:05:57 -07001297 if (!bucket || !rt6_ex)
1298 return;
Colin Ian Kingb2427e62017-10-10 18:01:16 +01001299
1300 net = dev_net(rt6_ex->rt6i->dst.dev);
Paolo Abenif5b51fe2019-02-20 18:18:12 +01001301 net->ipv6.rt6_stats->fib_rt_cache--;
1302
1303 /* purge completely the exception to allow releasing the held resources:
1304 * some [sk] cache may keep the dst around for unlimited time
1305 */
1306 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1307 lockdep_is_held(&rt6_exception_lock));
1308 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1309 fib6_info_release(from);
1310 dst_dev_put(&rt6_ex->rt6i->dst);
1311
Wei Wang35732d02017-10-06 12:05:57 -07001312 hlist_del_rcu(&rt6_ex->hlist);
David Ahern77634cc2018-04-17 17:33:27 -07001313 dst_release(&rt6_ex->rt6i->dst);
Wei Wang35732d02017-10-06 12:05:57 -07001314 kfree_rcu(rt6_ex, rcu);
1315 WARN_ON_ONCE(!bucket->depth);
1316 bucket->depth--;
1317}
1318
1319/* Remove oldest rt6_ex in bucket and free the memory
1320 * Caller must hold rt6_exception_lock
1321 */
1322static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket)
1323{
1324 struct rt6_exception *rt6_ex, *oldest = NULL;
1325
1326 if (!bucket)
1327 return;
1328
1329 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1330 if (!oldest || time_before(rt6_ex->stamp, oldest->stamp))
1331 oldest = rt6_ex;
1332 }
1333 rt6_remove_exception(bucket, oldest);
1334}
1335
1336static u32 rt6_exception_hash(const struct in6_addr *dst,
1337 const struct in6_addr *src)
1338{
1339 static u32 seed __read_mostly;
1340 u32 val;
1341
1342 net_get_random_once(&seed, sizeof(seed));
1343 val = jhash(dst, sizeof(*dst), seed);
1344
1345#ifdef CONFIG_IPV6_SUBTREES
1346 if (src)
1347 val = jhash(src, sizeof(*src), val);
1348#endif
1349 return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT);
1350}
1351
1352/* Helper function to find the cached rt in the hash table
1353 * and update bucket pointer to point to the bucket for this
1354 * (daddr, saddr) pair
1355 * Caller must hold rt6_exception_lock
1356 */
1357static struct rt6_exception *
1358__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket,
1359 const struct in6_addr *daddr,
1360 const struct in6_addr *saddr)
1361{
1362 struct rt6_exception *rt6_ex;
1363 u32 hval;
1364
1365 if (!(*bucket) || !daddr)
1366 return NULL;
1367
1368 hval = rt6_exception_hash(daddr, saddr);
1369 *bucket += hval;
1370
1371 hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) {
1372 struct rt6_info *rt6 = rt6_ex->rt6i;
1373 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1374
1375#ifdef CONFIG_IPV6_SUBTREES
1376 if (matched && saddr)
1377 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1378#endif
1379 if (matched)
1380 return rt6_ex;
1381 }
1382 return NULL;
1383}
1384
1385/* Helper function to find the cached rt in the hash table
1386 * and update bucket pointer to point to the bucket for this
1387 * (daddr, saddr) pair
1388 * Caller must hold rcu_read_lock()
1389 */
1390static struct rt6_exception *
1391__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
1392 const struct in6_addr *daddr,
1393 const struct in6_addr *saddr)
1394{
1395 struct rt6_exception *rt6_ex;
1396 u32 hval;
1397
1398 WARN_ON_ONCE(!rcu_read_lock_held());
1399
1400 if (!(*bucket) || !daddr)
1401 return NULL;
1402
1403 hval = rt6_exception_hash(daddr, saddr);
1404 *bucket += hval;
1405
1406 hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) {
1407 struct rt6_info *rt6 = rt6_ex->rt6i;
1408 bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr);
1409
1410#ifdef CONFIG_IPV6_SUBTREES
1411 if (matched && saddr)
1412 matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr);
1413#endif
1414 if (matched)
1415 return rt6_ex;
1416 }
1417 return NULL;
1418}
1419
David Ahern8d1c8022018-04-17 17:33:26 -07001420static unsigned int fib6_mtu(const struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001421{
David Ahernd4ead6b2018-04-17 17:33:16 -07001422 unsigned int mtu;
1423
David Aherndcd1f572018-04-18 15:39:05 -07001424 if (rt->fib6_pmtu) {
1425 mtu = rt->fib6_pmtu;
1426 } else {
1427 struct net_device *dev = fib6_info_nh_dev(rt);
1428 struct inet6_dev *idev;
1429
1430 rcu_read_lock();
1431 idev = __in6_dev_get(dev);
1432 mtu = idev->cnf.mtu6;
1433 rcu_read_unlock();
1434 }
1435
David Ahernd4ead6b2018-04-17 17:33:16 -07001436 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
1437
David Ahernad1601a2019-03-27 20:53:56 -07001438 return mtu - lwtunnel_headroom(rt->fib6_nh.fib_nh_lws, mtu);
David Ahernd4ead6b2018-04-17 17:33:16 -07001439}
1440
Wei Wang35732d02017-10-06 12:05:57 -07001441static int rt6_insert_exception(struct rt6_info *nrt,
David Ahern8d1c8022018-04-17 17:33:26 -07001442 struct fib6_info *ort)
Wei Wang35732d02017-10-06 12:05:57 -07001443{
David Ahern5e670d82018-04-17 17:33:14 -07001444 struct net *net = dev_net(nrt->dst.dev);
Wei Wang35732d02017-10-06 12:05:57 -07001445 struct rt6_exception_bucket *bucket;
1446 struct in6_addr *src_key = NULL;
1447 struct rt6_exception *rt6_ex;
1448 int err = 0;
1449
Wei Wang35732d02017-10-06 12:05:57 -07001450 spin_lock_bh(&rt6_exception_lock);
1451
1452 if (ort->exception_bucket_flushed) {
1453 err = -EINVAL;
1454 goto out;
1455 }
1456
1457 bucket = rcu_dereference_protected(ort->rt6i_exception_bucket,
1458 lockdep_is_held(&rt6_exception_lock));
1459 if (!bucket) {
1460 bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket),
1461 GFP_ATOMIC);
1462 if (!bucket) {
1463 err = -ENOMEM;
1464 goto out;
1465 }
1466 rcu_assign_pointer(ort->rt6i_exception_bucket, bucket);
1467 }
1468
1469#ifdef CONFIG_IPV6_SUBTREES
1470 /* rt6i_src.plen != 0 indicates ort is in subtree
1471 * and exception table is indexed by a hash of
1472 * both rt6i_dst and rt6i_src.
1473 * Otherwise, the exception table is indexed by
1474 * a hash of only rt6i_dst.
1475 */
David Ahern93c2fb22018-04-18 15:38:59 -07001476 if (ort->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001477 src_key = &nrt->rt6i_src.addr;
1478#endif
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001479 /* rt6_mtu_change() might lower mtu on ort.
1480 * Only insert this exception route if its mtu
1481 * is less than ort's mtu value.
1482 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001483 if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) {
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001484 err = -EINVAL;
1485 goto out;
1486 }
Wei Wang60006a42017-10-06 12:05:58 -07001487
Wei Wang35732d02017-10-06 12:05:57 -07001488 rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr,
1489 src_key);
1490 if (rt6_ex)
1491 rt6_remove_exception(bucket, rt6_ex);
1492
1493 rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC);
1494 if (!rt6_ex) {
1495 err = -ENOMEM;
1496 goto out;
1497 }
1498 rt6_ex->rt6i = nrt;
1499 rt6_ex->stamp = jiffies;
Wei Wang35732d02017-10-06 12:05:57 -07001500 hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain);
1501 bucket->depth++;
Wei Wang81eb8442017-10-06 12:06:11 -07001502 net->ipv6.rt6_stats->fib_rt_cache++;
Wei Wang35732d02017-10-06 12:05:57 -07001503
1504 if (bucket->depth > FIB6_MAX_DEPTH)
1505 rt6_exception_remove_oldest(bucket);
1506
1507out:
1508 spin_unlock_bh(&rt6_exception_lock);
1509
1510 /* Update fn->fn_sernum to invalidate all cached dst */
Paolo Abenib886d5f2017-10-19 16:07:10 +02001511 if (!err) {
David Ahern93c2fb22018-04-18 15:38:59 -07001512 spin_lock_bh(&ort->fib6_table->tb6_lock);
David Ahern7aef6852018-04-17 17:33:10 -07001513 fib6_update_sernum(net, ort);
David Ahern93c2fb22018-04-18 15:38:59 -07001514 spin_unlock_bh(&ort->fib6_table->tb6_lock);
Paolo Abenib886d5f2017-10-19 16:07:10 +02001515 fib6_force_start_gc(net);
1516 }
Wei Wang35732d02017-10-06 12:05:57 -07001517
1518 return err;
1519}
1520
David Ahern8d1c8022018-04-17 17:33:26 -07001521void rt6_flush_exceptions(struct fib6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001522{
1523 struct rt6_exception_bucket *bucket;
1524 struct rt6_exception *rt6_ex;
1525 struct hlist_node *tmp;
1526 int i;
1527
1528 spin_lock_bh(&rt6_exception_lock);
1529 /* Prevent rt6_insert_exception() to recreate the bucket list */
1530 rt->exception_bucket_flushed = 1;
1531
1532 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1533 lockdep_is_held(&rt6_exception_lock));
1534 if (!bucket)
1535 goto out;
1536
1537 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1538 hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist)
1539 rt6_remove_exception(bucket, rt6_ex);
1540 WARN_ON_ONCE(bucket->depth);
1541 bucket++;
1542 }
1543
1544out:
1545 spin_unlock_bh(&rt6_exception_lock);
1546}
1547
1548/* Find cached rt in the hash table inside passed in rt
1549 * Caller has to hold rcu_read_lock()
1550 */
David Ahern7e4b5122019-04-16 14:36:00 -07001551static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
Wei Wang35732d02017-10-06 12:05:57 -07001552 struct in6_addr *daddr,
1553 struct in6_addr *saddr)
1554{
1555 struct rt6_exception_bucket *bucket;
1556 struct in6_addr *src_key = NULL;
1557 struct rt6_exception *rt6_ex;
David Ahern7e4b5122019-04-16 14:36:00 -07001558 struct rt6_info *ret = NULL;
Wei Wang35732d02017-10-06 12:05:57 -07001559
David Ahern7e4b5122019-04-16 14:36:00 -07001560 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
Wei Wang35732d02017-10-06 12:05:57 -07001561
1562#ifdef CONFIG_IPV6_SUBTREES
David Ahern7e4b5122019-04-16 14:36:00 -07001563 /* fib6i_src.plen != 0 indicates f6i is in subtree
Wei Wang35732d02017-10-06 12:05:57 -07001564 * and exception table is indexed by a hash of
David Ahern7e4b5122019-04-16 14:36:00 -07001565 * both fib6_dst and fib6_src.
Wei Wang35732d02017-10-06 12:05:57 -07001566 * Otherwise, the exception table is indexed by
David Ahern7e4b5122019-04-16 14:36:00 -07001567 * a hash of only fib6_dst.
Wei Wang35732d02017-10-06 12:05:57 -07001568 */
David Ahern7e4b5122019-04-16 14:36:00 -07001569 if (res->f6i->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001570 src_key = saddr;
1571#endif
1572 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1573
1574 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
David Ahern7e4b5122019-04-16 14:36:00 -07001575 ret = rt6_ex->rt6i;
Wei Wang35732d02017-10-06 12:05:57 -07001576
David Ahern7e4b5122019-04-16 14:36:00 -07001577 return ret;
Wei Wang35732d02017-10-06 12:05:57 -07001578}
1579
1580/* Remove the passed in cached rt from the hash table that contains it */
David Ahern23fb93a2018-04-17 17:33:23 -07001581static int rt6_remove_exception_rt(struct rt6_info *rt)
Wei Wang35732d02017-10-06 12:05:57 -07001582{
Wei Wang35732d02017-10-06 12:05:57 -07001583 struct rt6_exception_bucket *bucket;
1584 struct in6_addr *src_key = NULL;
1585 struct rt6_exception *rt6_ex;
David Ahern8a14e462018-04-23 11:32:07 -07001586 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001587 int err;
1588
Eric Dumazet091311d2018-04-24 09:22:49 -07001589 from = rcu_dereference(rt->from);
Wei Wang35732d02017-10-06 12:05:57 -07001590 if (!from ||
Colin Ian King442d7132017-10-10 19:10:30 +01001591 !(rt->rt6i_flags & RTF_CACHE))
Wei Wang35732d02017-10-06 12:05:57 -07001592 return -EINVAL;
1593
1594 if (!rcu_access_pointer(from->rt6i_exception_bucket))
1595 return -ENOENT;
1596
1597 spin_lock_bh(&rt6_exception_lock);
1598 bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
1599 lockdep_is_held(&rt6_exception_lock));
1600#ifdef CONFIG_IPV6_SUBTREES
1601 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1602 * and exception table is indexed by a hash of
1603 * both rt6i_dst and rt6i_src.
1604 * Otherwise, the exception table is indexed by
1605 * a hash of only rt6i_dst.
1606 */
David Ahern93c2fb22018-04-18 15:38:59 -07001607 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001608 src_key = &rt->rt6i_src.addr;
1609#endif
1610 rt6_ex = __rt6_find_exception_spinlock(&bucket,
1611 &rt->rt6i_dst.addr,
1612 src_key);
1613 if (rt6_ex) {
1614 rt6_remove_exception(bucket, rt6_ex);
1615 err = 0;
1616 } else {
1617 err = -ENOENT;
1618 }
1619
1620 spin_unlock_bh(&rt6_exception_lock);
1621 return err;
1622}
1623
1624/* Find rt6_ex which contains the passed in rt cache and
1625 * refresh its stamp
1626 */
1627static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
1628{
Wei Wang35732d02017-10-06 12:05:57 -07001629 struct rt6_exception_bucket *bucket;
1630 struct in6_addr *src_key = NULL;
1631 struct rt6_exception *rt6_ex;
Paolo Abeni193f3682019-02-21 11:19:41 +01001632 struct fib6_info *from;
Wei Wang35732d02017-10-06 12:05:57 -07001633
1634 rcu_read_lock();
Paolo Abeni193f3682019-02-21 11:19:41 +01001635 from = rcu_dereference(rt->from);
1636 if (!from || !(rt->rt6i_flags & RTF_CACHE))
1637 goto unlock;
1638
Wei Wang35732d02017-10-06 12:05:57 -07001639 bucket = rcu_dereference(from->rt6i_exception_bucket);
1640
1641#ifdef CONFIG_IPV6_SUBTREES
1642 /* rt6i_src.plen != 0 indicates 'from' is in subtree
1643 * and exception table is indexed by a hash of
1644 * both rt6i_dst and rt6i_src.
1645 * Otherwise, the exception table is indexed by
1646 * a hash of only rt6i_dst.
1647 */
David Ahern93c2fb22018-04-18 15:38:59 -07001648 if (from->fib6_src.plen)
Wei Wang35732d02017-10-06 12:05:57 -07001649 src_key = &rt->rt6i_src.addr;
1650#endif
1651 rt6_ex = __rt6_find_exception_rcu(&bucket,
1652 &rt->rt6i_dst.addr,
1653 src_key);
1654 if (rt6_ex)
1655 rt6_ex->stamp = jiffies;
1656
Paolo Abeni193f3682019-02-21 11:19:41 +01001657unlock:
Wei Wang35732d02017-10-06 12:05:57 -07001658 rcu_read_unlock();
1659}
1660
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001661static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev,
1662 struct rt6_info *rt, int mtu)
1663{
1664 /* If the new MTU is lower than the route PMTU, this new MTU will be the
1665 * lowest MTU in the path: always allow updating the route PMTU to
1666 * reflect PMTU decreases.
1667 *
1668 * If the new MTU is higher, and the route PMTU is equal to the local
1669 * MTU, this means the old MTU is the lowest in the path, so allow
1670 * updating it: if other nodes now have lower MTUs, PMTU discovery will
1671 * handle this.
1672 */
1673
1674 if (dst_mtu(&rt->dst) >= mtu)
1675 return true;
1676
1677 if (dst_mtu(&rt->dst) == idev->cnf.mtu6)
1678 return true;
1679
1680 return false;
1681}
1682
1683static void rt6_exceptions_update_pmtu(struct inet6_dev *idev,
David Ahern8d1c8022018-04-17 17:33:26 -07001684 struct fib6_info *rt, int mtu)
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001685{
1686 struct rt6_exception_bucket *bucket;
1687 struct rt6_exception *rt6_ex;
1688 int i;
1689
1690 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1691 lockdep_is_held(&rt6_exception_lock));
1692
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001693 if (!bucket)
1694 return;
1695
1696 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1697 hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
1698 struct rt6_info *entry = rt6_ex->rt6i;
1699
1700 /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected
David Ahernd4ead6b2018-04-17 17:33:16 -07001701 * route), the metrics of its rt->from have already
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001702 * been updated.
1703 */
David Ahernd4ead6b2018-04-17 17:33:16 -07001704 if (dst_metric_raw(&entry->dst, RTAX_MTU) &&
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001705 rt6_mtu_change_route_allowed(idev, entry, mtu))
David Ahernd4ead6b2018-04-17 17:33:16 -07001706 dst_metric_set(&entry->dst, RTAX_MTU, mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001707 }
Stefano Brivioe9fa1492018-03-06 11:10:19 +01001708 bucket++;
Wei Wangf5bbe7e2017-10-06 12:05:59 -07001709 }
1710}
1711
Wei Wangb16cb452017-10-06 12:06:00 -07001712#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE)
1713
David Ahern8d1c8022018-04-17 17:33:26 -07001714static void rt6_exceptions_clean_tohost(struct fib6_info *rt,
Wei Wangb16cb452017-10-06 12:06:00 -07001715 struct in6_addr *gateway)
1716{
1717 struct rt6_exception_bucket *bucket;
1718 struct rt6_exception *rt6_ex;
1719 struct hlist_node *tmp;
1720 int i;
1721
1722 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1723 return;
1724
1725 spin_lock_bh(&rt6_exception_lock);
1726 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1727 lockdep_is_held(&rt6_exception_lock));
1728
1729 if (bucket) {
1730 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1731 hlist_for_each_entry_safe(rt6_ex, tmp,
1732 &bucket->chain, hlist) {
1733 struct rt6_info *entry = rt6_ex->rt6i;
1734
1735 if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) ==
1736 RTF_CACHE_GATEWAY &&
1737 ipv6_addr_equal(gateway,
1738 &entry->rt6i_gateway)) {
1739 rt6_remove_exception(bucket, rt6_ex);
1740 }
1741 }
1742 bucket++;
1743 }
1744 }
1745
1746 spin_unlock_bh(&rt6_exception_lock);
1747}
1748
Wei Wangc757faa2017-10-06 12:06:01 -07001749static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
1750 struct rt6_exception *rt6_ex,
1751 struct fib6_gc_args *gc_args,
1752 unsigned long now)
1753{
1754 struct rt6_info *rt = rt6_ex->rt6i;
1755
Paolo Abeni1859bac2017-10-19 16:07:11 +02001756 /* we are pruning and obsoleting aged-out and non gateway exceptions
1757 * even if others have still references to them, so that on next
1758 * dst_check() such references can be dropped.
1759 * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when
1760 * expired, independently from their aging, as per RFC 8201 section 4
1761 */
Wei Wang31afeb42018-01-26 11:40:17 -08001762 if (!(rt->rt6i_flags & RTF_EXPIRES)) {
1763 if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1764 RT6_TRACE("aging clone %p\n", rt);
1765 rt6_remove_exception(bucket, rt6_ex);
1766 return;
1767 }
1768 } else if (time_after(jiffies, rt->dst.expires)) {
1769 RT6_TRACE("purging expired route %p\n", rt);
Wei Wangc757faa2017-10-06 12:06:01 -07001770 rt6_remove_exception(bucket, rt6_ex);
1771 return;
Wei Wang31afeb42018-01-26 11:40:17 -08001772 }
1773
1774 if (rt->rt6i_flags & RTF_GATEWAY) {
Wei Wangc757faa2017-10-06 12:06:01 -07001775 struct neighbour *neigh;
1776 __u8 neigh_flags = 0;
1777
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001778 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
1779 if (neigh)
Wei Wangc757faa2017-10-06 12:06:01 -07001780 neigh_flags = neigh->flags;
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001781
Wei Wangc757faa2017-10-06 12:06:01 -07001782 if (!(neigh_flags & NTF_ROUTER)) {
1783 RT6_TRACE("purging route %p via non-router but gateway\n",
1784 rt);
1785 rt6_remove_exception(bucket, rt6_ex);
1786 return;
1787 }
1788 }
Wei Wang31afeb42018-01-26 11:40:17 -08001789
Wei Wangc757faa2017-10-06 12:06:01 -07001790 gc_args->more++;
1791}
1792
David Ahern8d1c8022018-04-17 17:33:26 -07001793void rt6_age_exceptions(struct fib6_info *rt,
Wei Wangc757faa2017-10-06 12:06:01 -07001794 struct fib6_gc_args *gc_args,
1795 unsigned long now)
1796{
1797 struct rt6_exception_bucket *bucket;
1798 struct rt6_exception *rt6_ex;
1799 struct hlist_node *tmp;
1800 int i;
1801
1802 if (!rcu_access_pointer(rt->rt6i_exception_bucket))
1803 return;
1804
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001805 rcu_read_lock_bh();
1806 spin_lock(&rt6_exception_lock);
Wei Wangc757faa2017-10-06 12:06:01 -07001807 bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
1808 lockdep_is_held(&rt6_exception_lock));
1809
1810 if (bucket) {
1811 for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
1812 hlist_for_each_entry_safe(rt6_ex, tmp,
1813 &bucket->chain, hlist) {
1814 rt6_age_examine_exception(bucket, rt6_ex,
1815 gc_args, now);
1816 }
1817 bucket++;
1818 }
1819 }
Eric Dumazet1bfa26f2018-03-23 07:56:58 -07001820 spin_unlock(&rt6_exception_lock);
1821 rcu_read_unlock_bh();
Wei Wangc757faa2017-10-06 12:06:01 -07001822}
1823
David Ahern1d053da2018-05-09 20:34:21 -07001824/* must be called with rcu lock held */
1825struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
1826 int oif, struct flowi6 *fl6, int strict)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827{
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001828 struct fib6_node *fn, *saved_fn;
David Ahern8d1c8022018-04-17 17:33:26 -07001829 struct fib6_info *f6i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
David Ahern64547432018-05-09 20:34:19 -07001831 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001832 saved_fn = fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
David Ahernca254492015-10-12 11:47:10 -07001834 if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
1835 oif = 0;
1836
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001837redo_rt6_select:
David Ahern23fb93a2018-04-17 17:33:23 -07001838 f6i = rt6_select(net, fn, oif, strict);
David Ahern23fb93a2018-04-17 17:33:23 -07001839 if (f6i == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001840 fn = fib6_backtrack(fn, &fl6->saddr);
1841 if (fn)
1842 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001843 else if (strict & RT6_LOOKUP_F_REACHABLE) {
1844 /* also consider unreachable route */
1845 strict &= ~RT6_LOOKUP_F_REACHABLE;
1846 fn = saved_fn;
1847 goto redo_rt6_select;
Martin KaFai Lau367efcb2014-10-20 13:42:45 -07001848 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07001849 }
1850
David Ahernd4bea422018-05-09 20:34:24 -07001851 trace_fib6_table_lookup(net, f6i, table, fl6);
YOSHIFUJI Hideakifb9de912006-03-20 16:59:08 -08001852
David Ahern1d053da2018-05-09 20:34:21 -07001853 return f6i;
1854}
1855
1856struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
1857 int oif, struct flowi6 *fl6,
1858 const struct sk_buff *skb, int flags)
1859{
David Ahernb1d40992019-04-16 14:35:59 -07001860 struct fib6_result res = {};
David Ahern1d053da2018-05-09 20:34:21 -07001861 struct rt6_info *rt;
1862 int strict = 0;
1863
1864 strict |= flags & RT6_LOOKUP_F_IFACE;
1865 strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
1866 if (net->ipv6.devconf_all->forwarding == 0)
1867 strict |= RT6_LOOKUP_F_REACHABLE;
1868
1869 rcu_read_lock();
1870
David Ahernb1d40992019-04-16 14:35:59 -07001871 res.f6i = fib6_table_lookup(net, table, oif, fl6, strict);
1872 if (res.f6i == net->ipv6.fib6_null_entry) {
David Ahern421842e2018-04-17 17:33:18 -07001873 rt = net->ipv6.ip6_null_entry;
Wei Wang66f5d6c2017-10-06 12:06:10 -07001874 rcu_read_unlock();
Wei Wangd3843fe2017-10-06 12:06:06 -07001875 dst_hold(&rt->dst);
Wei Wangd3843fe2017-10-06 12:06:06 -07001876 return rt;
David Ahern23fb93a2018-04-17 17:33:23 -07001877 }
1878
David Ahernb1d40992019-04-16 14:35:59 -07001879 fib6_select_path(net, &res, fl6, oif, false, skb, strict);
David Ahernd83009d2019-04-09 14:41:17 -07001880
David Ahern23fb93a2018-04-17 17:33:23 -07001881 /*Search through exception table */
David Ahern7e4b5122019-04-16 14:36:00 -07001882 rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr);
David Ahern23fb93a2018-04-17 17:33:23 -07001883 if (rt) {
David Ahern10585b42019-03-20 09:24:50 -07001884 if (ip6_hold_safe(net, &rt))
Wei Wangd3843fe2017-10-06 12:06:06 -07001885 dst_use_noref(&rt->dst, jiffies);
David Ahernd4ead6b2018-04-17 17:33:16 -07001886
Wei Wang66f5d6c2017-10-06 12:06:10 -07001887 rcu_read_unlock();
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001888 return rt;
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001889 } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
David Ahernb1d40992019-04-16 14:35:59 -07001890 !res.nh->fib_nh_gw_family)) {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001891 /* Create a RTF_CACHE clone which will not be
1892 * owned by the fib6 tree. It is for the special case where
1893 * the daddr in the skb during the neighbor look-up is different
1894 * from the fl6->daddr used to look-up route here.
1895 */
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001896 struct rt6_info *uncached_rt;
1897
David Ahern85bd05d2019-04-16 14:36:01 -07001898 uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001899
David Ahern4d85cd02018-04-20 15:37:59 -07001900 rcu_read_unlock();
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001901
Wei Wang1cfb71e2017-06-17 10:42:33 -07001902 if (uncached_rt) {
1903 /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
1904 * No need for another dst_hold()
1905 */
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07001906 rt6_uncached_list_add(uncached_rt);
Wei Wang81eb8442017-10-06 12:06:11 -07001907 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Wei Wang1cfb71e2017-06-17 10:42:33 -07001908 } else {
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001909 uncached_rt = net->ipv6.ip6_null_entry;
Wei Wang1cfb71e2017-06-17 10:42:33 -07001910 dst_hold(&uncached_rt->dst);
1911 }
David Ahernb8115802015-11-19 12:24:22 -08001912
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07001913 return uncached_rt;
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001914 } else {
1915 /* Get a percpu copy */
1916
1917 struct rt6_info *pcpu_rt;
1918
Eric Dumazet951f7882017-10-08 21:07:18 -07001919 local_bh_disable();
David Aherndb3fede2019-04-16 14:36:03 -07001920 pcpu_rt = rt6_get_pcpu_route(&res);
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001921
David Ahern93531c62018-04-17 17:33:25 -07001922 if (!pcpu_rt)
David Aherndb3fede2019-04-16 14:36:03 -07001923 pcpu_rt = rt6_make_pcpu_route(net, &res);
David Ahern93531c62018-04-17 17:33:25 -07001924
Eric Dumazet951f7882017-10-08 21:07:18 -07001925 local_bh_enable();
1926 rcu_read_unlock();
David Ahernd4bea422018-05-09 20:34:24 -07001927
Martin KaFai Laud52d3992015-05-22 20:56:06 -07001928 return pcpu_rt;
1929 }
Thomas Grafc71099a2006-08-04 23:20:06 -07001930}
David Ahern9ff74382016-06-13 13:44:19 -07001931EXPORT_SYMBOL_GPL(ip6_pol_route);
Thomas Grafc71099a2006-08-04 23:20:06 -07001932
David Ahernb75cc8f2018-03-02 08:32:17 -08001933static struct rt6_info *ip6_pol_route_input(struct net *net,
1934 struct fib6_table *table,
1935 struct flowi6 *fl6,
1936 const struct sk_buff *skb,
1937 int flags)
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001938{
David Ahernb75cc8f2018-03-02 08:32:17 -08001939 return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags);
Pavel Emelyanov4acad722007-10-15 13:02:51 -07001940}
1941
Mahesh Bandeward409b842016-09-16 12:59:08 -07001942struct dst_entry *ip6_route_input_lookup(struct net *net,
1943 struct net_device *dev,
David Ahernb75cc8f2018-03-02 08:32:17 -08001944 struct flowi6 *fl6,
1945 const struct sk_buff *skb,
1946 int flags)
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001947{
1948 if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)
1949 flags |= RT6_LOOKUP_F_IFACE;
1950
David Ahernb75cc8f2018-03-02 08:32:17 -08001951 return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001952}
Mahesh Bandeward409b842016-09-16 12:59:08 -07001953EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00001954
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001955static void ip6_multipath_l3_keys(const struct sk_buff *skb,
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001956 struct flow_keys *keys,
1957 struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001958{
1959 const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
1960 const struct ipv6hdr *key_iph = outer_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001961 struct flow_keys *_flkeys = flkeys;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001962 const struct ipv6hdr *inner_iph;
1963 const struct icmp6hdr *icmph;
1964 struct ipv6hdr _inner_iph;
Eric Dumazetcea67a22018-04-29 09:54:59 -07001965 struct icmp6hdr _icmph;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001966
1967 if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
1968 goto out;
1969
Eric Dumazetcea67a22018-04-29 09:54:59 -07001970 icmph = skb_header_pointer(skb, skb_transport_offset(skb),
1971 sizeof(_icmph), &_icmph);
1972 if (!icmph)
1973 goto out;
1974
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001975 if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
1976 icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
1977 icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
1978 icmph->icmp6_type != ICMPV6_PARAMPROB)
1979 goto out;
1980
1981 inner_iph = skb_header_pointer(skb,
1982 skb_transport_offset(skb) + sizeof(*icmph),
1983 sizeof(_inner_iph), &_inner_iph);
1984 if (!inner_iph)
1985 goto out;
1986
1987 key_iph = inner_iph;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001988 _flkeys = NULL;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02001989out:
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001990 if (_flkeys) {
1991 keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
1992 keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
1993 keys->tags.flow_label = _flkeys->tags.flow_label;
1994 keys->basic.ip_proto = _flkeys->basic.ip_proto;
1995 } else {
1996 keys->addrs.v6addrs.src = key_iph->saddr;
1997 keys->addrs.v6addrs.dst = key_iph->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02001998 keys->tags.flow_label = ip6_flowlabel(key_iph);
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05001999 keys->basic.ip_proto = key_iph->nexthdr;
2000 }
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002001}
2002
2003/* if skb is set it will be used and fl6 can be NULL */
David Ahernb4bac172018-03-02 08:32:18 -08002004u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
2005 const struct sk_buff *skb, struct flow_keys *flkeys)
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002006{
2007 struct flow_keys hash_keys;
David Ahern9a2a5372018-03-02 08:32:15 -08002008 u32 mhash;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002009
David S. Millerbbfa0472018-03-12 11:09:33 -04002010 switch (ip6_multipath_hash_policy(net)) {
David Ahernb4bac172018-03-02 08:32:18 -08002011 case 0:
2012 memset(&hash_keys, 0, sizeof(hash_keys));
2013 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2014 if (skb) {
2015 ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
2016 } else {
2017 hash_keys.addrs.v6addrs.src = fl6->saddr;
2018 hash_keys.addrs.v6addrs.dst = fl6->daddr;
Michal Kubecekfa1be7e2018-06-04 11:36:05 +02002019 hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
David Ahernb4bac172018-03-02 08:32:18 -08002020 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2021 }
2022 break;
2023 case 1:
2024 if (skb) {
2025 unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
2026 struct flow_keys keys;
2027
2028 /* short-circuit if we already have L4 hash present */
2029 if (skb->l4_hash)
2030 return skb_get_hash_raw(skb) >> 1;
2031
2032 memset(&hash_keys, 0, sizeof(hash_keys));
2033
2034 if (!flkeys) {
2035 skb_flow_dissect_flow_keys(skb, &keys, flag);
2036 flkeys = &keys;
2037 }
2038 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2039 hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
2040 hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
2041 hash_keys.ports.src = flkeys->ports.src;
2042 hash_keys.ports.dst = flkeys->ports.dst;
2043 hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
2044 } else {
2045 memset(&hash_keys, 0, sizeof(hash_keys));
2046 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
2047 hash_keys.addrs.v6addrs.src = fl6->saddr;
2048 hash_keys.addrs.v6addrs.dst = fl6->daddr;
2049 hash_keys.ports.src = fl6->fl6_sport;
2050 hash_keys.ports.dst = fl6->fl6_dport;
2051 hash_keys.basic.ip_proto = fl6->flowi6_proto;
2052 }
2053 break;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002054 }
David Ahern9a2a5372018-03-02 08:32:15 -08002055 mhash = flow_hash_from_keys(&hash_keys);
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002056
David Ahern9a2a5372018-03-02 08:32:15 -08002057 return mhash >> 1;
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002058}
2059
Thomas Grafc71099a2006-08-04 23:20:06 -07002060void ip6_route_input(struct sk_buff *skb)
2061{
Eric Dumazetb71d1d42011-04-22 04:53:02 +00002062 const struct ipv6hdr *iph = ipv6_hdr(skb);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002063 struct net *net = dev_net(skb->dev);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002064 int flags = RT6_LOOKUP_F_HAS_SADDR;
Jiri Benc904af042015-08-20 13:56:31 +02002065 struct ip_tunnel_info *tun_info;
David S. Miller4c9483b2011-03-12 16:22:43 -05002066 struct flowi6 fl6 = {
David Aherne0d56fd2016-09-10 12:09:57 -07002067 .flowi6_iif = skb->dev->ifindex,
David S. Miller4c9483b2011-03-12 16:22:43 -05002068 .daddr = iph->daddr,
2069 .saddr = iph->saddr,
YOSHIFUJI Hideaki / 吉藤英明6502ca52013-01-13 05:01:51 +00002070 .flowlabel = ip6_flowinfo(iph),
David S. Miller4c9483b2011-03-12 16:22:43 -05002071 .flowi6_mark = skb->mark,
2072 .flowi6_proto = iph->nexthdr,
Thomas Grafc71099a2006-08-04 23:20:06 -07002073 };
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002074 struct flow_keys *flkeys = NULL, _flkeys;
Thomas Grafadaa70b2006-10-13 15:01:03 -07002075
Jiri Benc904af042015-08-20 13:56:31 +02002076 tun_info = skb_tunnel_info(skb);
Jiri Benc46fa0622015-08-28 20:48:19 +02002077 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
Jiri Benc904af042015-08-20 13:56:31 +02002078 fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
Roopa Prabhu5e5d6fe2018-02-28 22:43:22 -05002079
2080 if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
2081 flkeys = &_flkeys;
2082
Jakub Sitnicki23aebda2017-08-23 09:58:29 +02002083 if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
David Ahernb4bac172018-03-02 08:32:18 -08002084 fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
Jiri Benc06e9d042015-08-20 13:56:26 +02002085 skb_dst_drop(skb);
David Ahernb75cc8f2018-03-02 08:32:17 -08002086 skb_dst_set(skb,
2087 ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
Thomas Grafc71099a2006-08-04 23:20:06 -07002088}
2089
David Ahernb75cc8f2018-03-02 08:32:17 -08002090static struct rt6_info *ip6_pol_route_output(struct net *net,
2091 struct fib6_table *table,
2092 struct flowi6 *fl6,
2093 const struct sk_buff *skb,
2094 int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002095{
David Ahernb75cc8f2018-03-02 08:32:17 -08002096 return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -07002097}
2098
Paolo Abeni6f21c962016-01-29 12:30:19 +01002099struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk,
2100 struct flowi6 *fl6, int flags)
Thomas Grafc71099a2006-08-04 23:20:06 -07002101{
David Ahernd46a9d62015-10-21 08:42:22 -07002102 bool any_src;
Thomas Grafc71099a2006-08-04 23:20:06 -07002103
Robert Shearman3ede0bb2018-09-19 13:56:53 +01002104 if (ipv6_addr_type(&fl6->daddr) &
2105 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) {
David Ahern4c1feac2016-09-10 12:09:56 -07002106 struct dst_entry *dst;
2107
2108 dst = l3mdev_link_scope_lookup(net, fl6);
2109 if (dst)
2110 return dst;
2111 }
David Ahernca254492015-10-12 11:47:10 -07002112
Pavel Emelyanov1fb94892012-08-08 21:53:36 +00002113 fl6->flowi6_iif = LOOPBACK_IFINDEX;
David McCullough4dc27d1c2012-06-25 15:42:26 +00002114
David Ahernd46a9d62015-10-21 08:42:22 -07002115 any_src = ipv6_addr_any(&fl6->saddr);
David Ahern741a11d2015-09-28 10:12:13 -07002116 if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) ||
David Ahernd46a9d62015-10-21 08:42:22 -07002117 (fl6->flowi6_oif && any_src))
YOSHIFUJI Hideaki77d16f42006-08-23 17:25:05 -07002118 flags |= RT6_LOOKUP_F_IFACE;
Thomas Grafc71099a2006-08-04 23:20:06 -07002119
David Ahernd46a9d62015-10-21 08:42:22 -07002120 if (!any_src)
Thomas Grafadaa70b2006-10-13 15:01:03 -07002121 flags |= RT6_LOOKUP_F_HAS_SADDR;
YOSHIFUJI Hideaki / 吉藤英明0c9a2ac2010-03-07 00:14:44 +00002122 else if (sk)
2123 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
Thomas Grafadaa70b2006-10-13 15:01:03 -07002124
David Ahernb75cc8f2018-03-02 08:32:17 -08002125 return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126}
Paolo Abeni6f21c962016-01-29 12:30:19 +01002127EXPORT_SYMBOL_GPL(ip6_route_output_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128
David S. Miller2774c132011-03-01 14:59:04 -08002129struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig)
David S. Miller14e50e52007-05-24 18:17:54 -07002130{
David S. Miller5c1e6aa2011-04-28 14:13:38 -07002131 struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig;
Wei Wang1dbe32522017-06-17 10:42:26 -07002132 struct net_device *loopback_dev = net->loopback_dev;
David S. Miller14e50e52007-05-24 18:17:54 -07002133 struct dst_entry *new = NULL;
2134
Wei Wang1dbe32522017-06-17 10:42:26 -07002135 rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1,
Steffen Klassert62cf27e2017-10-09 08:39:43 +02002136 DST_OBSOLETE_DEAD, 0);
David S. Miller14e50e52007-05-24 18:17:54 -07002137 if (rt) {
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002138 rt6_info_init(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002139 atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc);
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002140
Changli Gaod8d1f302010-06-10 23:31:35 -07002141 new = &rt->dst;
David S. Miller14e50e52007-05-24 18:17:54 -07002142 new->__use = 1;
Herbert Xu352e5122007-11-13 21:34:06 -08002143 new->input = dst_discard;
Eric W. Biedermanede20592015-10-07 16:48:47 -05002144 new->output = dst_discard_out;
David S. Miller14e50e52007-05-24 18:17:54 -07002145
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002146 dst_copy_metrics(new, &ort->dst);
David S. Miller14e50e52007-05-24 18:17:54 -07002147
Wei Wang1dbe32522017-06-17 10:42:26 -07002148 rt->rt6i_idev = in6_dev_get(loopback_dev);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00002149 rt->rt6i_gateway = ort->rt6i_gateway;
Martin KaFai Lau0a1f5962015-10-15 16:39:58 -07002150 rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU;
David S. Miller14e50e52007-05-24 18:17:54 -07002151
2152 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
2153#ifdef CONFIG_IPV6_SUBTREES
2154 memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
2155#endif
David S. Miller14e50e52007-05-24 18:17:54 -07002156 }
2157
David S. Miller69ead7a2011-03-01 14:45:33 -08002158 dst_release(dst_orig);
2159 return new ? new : ERR_PTR(-ENOMEM);
David S. Miller14e50e52007-05-24 18:17:54 -07002160}
David S. Miller14e50e52007-05-24 18:17:54 -07002161
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162/*
2163 * Destination cache support functions
2164 */
2165
David Ahern8d1c8022018-04-17 17:33:26 -07002166static bool fib6_check(struct fib6_info *f6i, u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002167{
Steffen Klassert36143642017-08-25 09:05:42 +02002168 u32 rt_cookie = 0;
Wei Wangc5cff852017-08-21 09:47:10 -07002169
David Ahern8ae86972018-04-20 15:38:03 -07002170 if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
David Ahern93531c62018-04-17 17:33:25 -07002171 return false;
2172
2173 if (fib6_check_expired(f6i))
2174 return false;
2175
2176 return true;
2177}
2178
David Aherna68886a2018-04-20 15:38:02 -07002179static struct dst_entry *rt6_check(struct rt6_info *rt,
2180 struct fib6_info *from,
2181 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002182{
Wei Wangc5cff852017-08-21 09:47:10 -07002183 u32 rt_cookie = 0;
2184
David Aherna68886a2018-04-20 15:38:02 -07002185 if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
David Ahern93531c62018-04-17 17:33:25 -07002186 rt_cookie != cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002187 return NULL;
2188
2189 if (rt6_check_expired(rt))
2190 return NULL;
2191
2192 return &rt->dst;
2193}
2194
David Aherna68886a2018-04-20 15:38:02 -07002195static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
2196 struct fib6_info *from,
2197 u32 cookie)
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002198{
Martin KaFai Lau5973fb12015-11-11 11:51:07 -08002199 if (!__rt6_check_expired(rt) &&
2200 rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
David Aherna68886a2018-04-20 15:38:02 -07002201 fib6_check(from, cookie))
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002202 return &rt->dst;
2203 else
2204 return NULL;
2205}
2206
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
2208{
David Aherna87b7dc2018-04-20 15:38:00 -07002209 struct dst_entry *dst_ret;
David Aherna68886a2018-04-20 15:38:02 -07002210 struct fib6_info *from;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 struct rt6_info *rt;
2212
David Aherna87b7dc2018-04-20 15:38:00 -07002213 rt = container_of(dst, struct rt6_info, dst);
2214
2215 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216
Nicolas Dichtel6f3118b2012-09-10 22:09:46 +00002217 /* All IPV6 dsts are created with ->obsolete set to the value
2218 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
2219 * into this function always.
2220 */
Hannes Frederic Sowae3bc10b2013-10-24 07:48:24 +02002221
David Aherna68886a2018-04-20 15:38:02 -07002222 from = rcu_dereference(rt->from);
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002223
David Aherna68886a2018-04-20 15:38:02 -07002224 if (from && (rt->rt6i_flags & RTF_PCPU ||
2225 unlikely(!list_empty(&rt->rt6i_uncached))))
2226 dst_ret = rt6_dst_from_check(rt, from, cookie);
Martin KaFai Lau3da59bd2015-05-22 20:56:03 -07002227 else
David Aherna68886a2018-04-20 15:38:02 -07002228 dst_ret = rt6_check(rt, from, cookie);
David Aherna87b7dc2018-04-20 15:38:00 -07002229
2230 rcu_read_unlock();
2231
2232 return dst_ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233}
2234
2235static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2236{
2237 struct rt6_info *rt = (struct rt6_info *) dst;
2238
2239 if (rt) {
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002240 if (rt->rt6i_flags & RTF_CACHE) {
David Ahernc3c14da2018-04-23 11:32:06 -07002241 rcu_read_lock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002242 if (rt6_check_expired(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07002243 rt6_remove_exception_rt(rt);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002244 dst = NULL;
2245 }
David Ahernc3c14da2018-04-23 11:32:06 -07002246 rcu_read_unlock();
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002247 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 dst_release(dst);
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002249 dst = NULL;
2250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 }
YOSHIFUJI Hideaki / 吉藤英明54c1a852010-03-28 07:15:45 +00002252 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253}
2254
2255static void ip6_link_failure(struct sk_buff *skb)
2256{
2257 struct rt6_info *rt;
2258
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00002259 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260
Eric Dumazetadf30902009-06-02 05:19:30 +00002261 rt = (struct rt6_info *) skb_dst(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 if (rt) {
David Ahern8a14e462018-04-23 11:32:07 -07002263 rcu_read_lock();
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002264 if (rt->rt6i_flags & RTF_CACHE) {
Xin Long761f6022018-11-14 00:48:28 +08002265 rt6_remove_exception_rt(rt);
Wei Wangc5cff852017-08-21 09:47:10 -07002266 } else {
David Aherna68886a2018-04-20 15:38:02 -07002267 struct fib6_info *from;
Wei Wangc5cff852017-08-21 09:47:10 -07002268 struct fib6_node *fn;
2269
David Aherna68886a2018-04-20 15:38:02 -07002270 from = rcu_dereference(rt->from);
2271 if (from) {
2272 fn = rcu_dereference(from->fib6_node);
2273 if (fn && (rt->rt6i_flags & RTF_DEFAULT))
2274 fn->fn_sernum = -1;
2275 }
Hannes Frederic Sowa1eb4f752013-07-10 23:00:57 +02002276 }
David Ahern8a14e462018-04-23 11:32:07 -07002277 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 }
2279}
2280
David Ahern6a3e0302018-04-20 15:37:57 -07002281static void rt6_update_expires(struct rt6_info *rt0, int timeout)
2282{
David Aherna68886a2018-04-20 15:38:02 -07002283 if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
2284 struct fib6_info *from;
2285
2286 rcu_read_lock();
2287 from = rcu_dereference(rt0->from);
2288 if (from)
2289 rt0->dst.expires = from->expires;
2290 rcu_read_unlock();
2291 }
David Ahern6a3e0302018-04-20 15:37:57 -07002292
2293 dst_set_expires(&rt0->dst, timeout);
2294 rt0->rt6i_flags |= RTF_EXPIRES;
2295}
2296
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002297static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
2298{
2299 struct net *net = dev_net(rt->dst.dev);
2300
David Ahernd4ead6b2018-04-17 17:33:16 -07002301 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002302 rt->rt6i_flags |= RTF_MODIFIED;
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002303 rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires);
2304}
2305
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002306static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
2307{
2308 return !(rt->rt6i_flags & RTF_CACHE) &&
Paolo Abeni1490ed22019-02-15 18:15:37 +01002309 (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002310}
2311
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002312static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
2313 const struct ipv6hdr *iph, u32 mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314{
Julian Anastasov0dec8792017-02-06 23:14:16 +02002315 const struct in6_addr *daddr, *saddr;
Ian Morris67ba4152014-08-24 21:53:10 +01002316 struct rt6_info *rt6 = (struct rt6_info *)dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
Xin Long19bda362016-10-28 18:18:01 +08002318 if (dst_metric_locked(dst, RTAX_MTU))
2319 return;
2320
Julian Anastasov0dec8792017-02-06 23:14:16 +02002321 if (iph) {
2322 daddr = &iph->daddr;
2323 saddr = &iph->saddr;
2324 } else if (sk) {
2325 daddr = &sk->sk_v6_daddr;
2326 saddr = &inet6_sk(sk)->saddr;
2327 } else {
2328 daddr = NULL;
2329 saddr = NULL;
2330 }
2331 dst_confirm_neigh(dst, daddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002332 mtu = max_t(u32, mtu, IPV6_MIN_MTU);
2333 if (mtu >= dst_mtu(dst))
2334 return;
David S. Miller81aded22012-06-15 14:54:11 -07002335
Martin KaFai Lau0d3f6d22015-11-11 11:51:06 -08002336 if (!rt6_cache_allowed_for_pmtu(rt6)) {
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002337 rt6_do_update_pmtu(rt6, mtu);
Wei Wang2b760fc2017-10-06 12:06:03 -07002338 /* update rt6_ex->stamp for cache */
2339 if (rt6->rt6i_flags & RTF_CACHE)
2340 rt6_update_exception_stamp_rt(rt6);
Julian Anastasov0dec8792017-02-06 23:14:16 +02002341 } else if (daddr) {
David Ahern85bd05d2019-04-16 14:36:01 -07002342 struct fib6_result res = {};
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002343 struct rt6_info *nrt6;
Hagen Paul Pfeifer9d289712015-01-15 22:34:25 +01002344
David Ahern4d85cd02018-04-20 15:37:59 -07002345 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07002346 res.f6i = rcu_dereference(rt6->from);
2347 if (!res.f6i) {
Jonathan Lemon9c69a132019-04-14 14:21:29 -07002348 rcu_read_unlock();
2349 return;
2350 }
David Ahern85bd05d2019-04-16 14:36:01 -07002351 res.nh = &res.f6i->fib6_nh;
2352 nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002353 if (nrt6) {
2354 rt6_do_update_pmtu(nrt6, mtu);
David Ahern85bd05d2019-04-16 14:36:01 -07002355 if (rt6_insert_exception(nrt6, res.f6i))
Wei Wang2b760fc2017-10-06 12:06:03 -07002356 dst_release_immediate(&nrt6->dst);
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002357 }
David Aherna68886a2018-04-20 15:38:02 -07002358 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359 }
2360}
2361
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002362static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
2363 struct sk_buff *skb, u32 mtu)
2364{
2365 __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu);
2366}
2367
David S. Miller42ae66c2012-06-15 20:01:57 -07002368void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002369 int oif, u32 mark, kuid_t uid)
David S. Miller81aded22012-06-15 14:54:11 -07002370{
2371 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2372 struct dst_entry *dst;
Maciej Żenczykowskidc920952018-09-29 23:44:51 -07002373 struct flowi6 fl6 = {
2374 .flowi6_oif = oif,
2375 .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark),
2376 .daddr = iph->daddr,
2377 .saddr = iph->saddr,
2378 .flowlabel = ip6_flowinfo(iph),
2379 .flowi6_uid = uid,
2380 };
David S. Miller81aded22012-06-15 14:54:11 -07002381
2382 dst = ip6_route_output(net, NULL, &fl6);
2383 if (!dst->error)
Martin KaFai Lau45e4fd22015-05-22 20:56:00 -07002384 __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu));
David S. Miller81aded22012-06-15 14:54:11 -07002385 dst_release(dst);
2386}
2387EXPORT_SYMBOL_GPL(ip6_update_pmtu);
2388
2389void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
2390{
David Ahern7ddacfa2018-11-18 10:45:30 -08002391 int oif = sk->sk_bound_dev_if;
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002392 struct dst_entry *dst;
2393
David Ahern7ddacfa2018-11-18 10:45:30 -08002394 if (!oif && skb->dev)
2395 oif = l3mdev_master_ifindex(skb->dev);
2396
2397 ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid);
Martin KaFai Lau33c162a2016-04-11 15:29:36 -07002398
2399 dst = __sk_dst_get(sk);
2400 if (!dst || !dst->obsolete ||
2401 dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
2402 return;
2403
2404 bh_lock_sock(sk);
2405 if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
2406 ip6_datagram_dst_update(sk, false);
2407 bh_unlock_sock(sk);
David S. Miller81aded22012-06-15 14:54:11 -07002408}
2409EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
2410
Alexey Kodanev7d6850f2018-04-03 15:00:07 +03002411void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,
2412 const struct flowi6 *fl6)
2413{
2414#ifdef CONFIG_IPV6_SUBTREES
2415 struct ipv6_pinfo *np = inet6_sk(sk);
2416#endif
2417
2418 ip6_dst_store(sk, dst,
2419 ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ?
2420 &sk->sk_v6_daddr : NULL,
2421#ifdef CONFIG_IPV6_SUBTREES
2422 ipv6_addr_equal(&fl6->saddr, &np->saddr) ?
2423 &np->saddr :
2424#endif
2425 NULL);
2426}
2427
David Ahern9b6b35a2019-04-16 14:36:02 -07002428static bool ip6_redirect_nh_match(const struct fib6_result *res,
David Ahern0b34eb02019-04-09 14:41:19 -07002429 struct flowi6 *fl6,
2430 const struct in6_addr *gw,
2431 struct rt6_info **ret)
2432{
David Ahern9b6b35a2019-04-16 14:36:02 -07002433 const struct fib6_nh *nh = res->nh;
2434
David Ahern0b34eb02019-04-09 14:41:19 -07002435 if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family ||
2436 fl6->flowi6_oif != nh->fib_nh_dev->ifindex)
2437 return false;
2438
2439 /* rt_cache's gateway might be different from its 'parent'
2440 * in the case of an ip redirect.
2441 * So we keep searching in the exception table if the gateway
2442 * is different.
2443 */
2444 if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) {
2445 struct rt6_info *rt_cache;
2446
David Ahern9b6b35a2019-04-16 14:36:02 -07002447 rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr);
David Ahern0b34eb02019-04-09 14:41:19 -07002448 if (rt_cache &&
2449 ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) {
2450 *ret = rt_cache;
2451 return true;
2452 }
2453 return false;
2454 }
2455 return true;
2456}
2457
Duan Jiongb55b76b2013-09-04 19:44:21 +08002458/* Handle redirects */
2459struct ip6rd_flowi {
2460 struct flowi6 fl6;
2461 struct in6_addr gateway;
2462};
2463
2464static struct rt6_info *__ip6_route_redirect(struct net *net,
2465 struct fib6_table *table,
2466 struct flowi6 *fl6,
David Ahernb75cc8f2018-03-02 08:32:17 -08002467 const struct sk_buff *skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002468 int flags)
2469{
2470 struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
David Ahern0b34eb02019-04-09 14:41:19 -07002471 struct rt6_info *ret = NULL;
David Ahern9b6b35a2019-04-16 14:36:02 -07002472 struct fib6_result res = {};
David Ahern8d1c8022018-04-17 17:33:26 -07002473 struct fib6_info *rt;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002474 struct fib6_node *fn;
2475
2476 /* Get the "current" route for this destination and
Alexander Alemayhu67c408c2017-01-07 23:53:00 +01002477 * check if the redirect has come from appropriate router.
Duan Jiongb55b76b2013-09-04 19:44:21 +08002478 *
2479 * RFC 4861 specifies that redirects should only be
2480 * accepted if they come from the nexthop to the target.
2481 * Due to the way the routes are chosen, this notion
2482 * is a bit fuzzy and one might need to check all possible
2483 * routes.
2484 */
2485
Wei Wang66f5d6c2017-10-06 12:06:10 -07002486 rcu_read_lock();
David Ahern64547432018-05-09 20:34:19 -07002487 fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002488restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07002489 for_each_fib6_node_rt_rcu(fn) {
David Ahern9b6b35a2019-04-16 14:36:02 -07002490 res.f6i = rt;
2491 res.nh = &rt->fib6_nh;
2492
David Ahern14895682018-04-17 17:33:17 -07002493 if (fib6_check_expired(rt))
Duan Jiongb55b76b2013-09-04 19:44:21 +08002494 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07002495 if (rt->fib6_flags & RTF_REJECT)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002496 break;
David Ahern9b6b35a2019-04-16 14:36:02 -07002497 if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, &ret))
David Ahern0b34eb02019-04-09 14:41:19 -07002498 goto out;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002499 }
2500
2501 if (!rt)
David Ahern421842e2018-04-17 17:33:18 -07002502 rt = net->ipv6.fib6_null_entry;
David Ahern93c2fb22018-04-18 15:38:59 -07002503 else if (rt->fib6_flags & RTF_REJECT) {
David Ahern23fb93a2018-04-17 17:33:23 -07002504 ret = net->ipv6.ip6_null_entry;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002505 goto out;
2506 }
2507
David Ahern421842e2018-04-17 17:33:18 -07002508 if (rt == net->ipv6.fib6_null_entry) {
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002509 fn = fib6_backtrack(fn, &fl6->saddr);
2510 if (fn)
2511 goto restart;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002512 }
Martin KaFai Laua3c00e42014-10-20 13:42:43 -07002513
David Ahern9b6b35a2019-04-16 14:36:02 -07002514 res.f6i = rt;
2515 res.nh = &rt->fib6_nh;
Martin KaFai Laub0a1ba52015-01-20 19:16:02 -08002516out:
David Ahern23fb93a2018-04-17 17:33:23 -07002517 if (ret)
David Ahern10585b42019-03-20 09:24:50 -07002518 ip6_hold_safe(net, &ret);
David Ahern23fb93a2018-04-17 17:33:23 -07002519 else
David Ahern9b6b35a2019-04-16 14:36:02 -07002520 ret = ip6_create_rt_rcu(&res);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002521
Wei Wang66f5d6c2017-10-06 12:06:10 -07002522 rcu_read_unlock();
Duan Jiongb55b76b2013-09-04 19:44:21 +08002523
Paolo Abenib65f1642017-10-19 09:31:43 +02002524 trace_fib6_table_lookup(net, rt, table, fl6);
David Ahern23fb93a2018-04-17 17:33:23 -07002525 return ret;
Duan Jiongb55b76b2013-09-04 19:44:21 +08002526};
2527
2528static struct dst_entry *ip6_route_redirect(struct net *net,
David Ahernb75cc8f2018-03-02 08:32:17 -08002529 const struct flowi6 *fl6,
2530 const struct sk_buff *skb,
2531 const struct in6_addr *gateway)
Duan Jiongb55b76b2013-09-04 19:44:21 +08002532{
2533 int flags = RT6_LOOKUP_F_HAS_SADDR;
2534 struct ip6rd_flowi rdfl;
2535
2536 rdfl.fl6 = *fl6;
2537 rdfl.gateway = *gateway;
2538
David Ahernb75cc8f2018-03-02 08:32:17 -08002539 return fib6_rule_lookup(net, &rdfl.fl6, skb,
Duan Jiongb55b76b2013-09-04 19:44:21 +08002540 flags, __ip6_route_redirect);
2541}
2542
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002543void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
2544 kuid_t uid)
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002545{
2546 const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
2547 struct dst_entry *dst;
Maciej Żenczykowski1f7f10a2018-09-29 23:44:48 -07002548 struct flowi6 fl6 = {
2549 .flowi6_iif = LOOPBACK_IFINDEX,
2550 .flowi6_oif = oif,
2551 .flowi6_mark = mark,
2552 .daddr = iph->daddr,
2553 .saddr = iph->saddr,
2554 .flowlabel = ip6_flowinfo(iph),
2555 .flowi6_uid = uid,
2556 };
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002557
David Ahernb75cc8f2018-03-02 08:32:17 -08002558 dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002559 rt6_do_redirect(dst, NULL, skb);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002560 dst_release(dst);
2561}
2562EXPORT_SYMBOL_GPL(ip6_redirect);
2563
Maciej Żenczykowskid4563362018-09-29 23:44:50 -07002564void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
Duan Jiongc92a59e2013-08-22 12:07:35 +08002565{
2566 const struct ipv6hdr *iph = ipv6_hdr(skb);
2567 const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
2568 struct dst_entry *dst;
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002569 struct flowi6 fl6 = {
2570 .flowi6_iif = LOOPBACK_IFINDEX,
2571 .flowi6_oif = oif,
Maciej Żenczykowski0b26fb12018-09-29 23:44:49 -07002572 .daddr = msg->dest,
2573 .saddr = iph->daddr,
2574 .flowi6_uid = sock_net_uid(net, NULL),
2575 };
Duan Jiongc92a59e2013-08-22 12:07:35 +08002576
David Ahernb75cc8f2018-03-02 08:32:17 -08002577 dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr);
Duan Jiongb55b76b2013-09-04 19:44:21 +08002578 rt6_do_redirect(dst, NULL, skb);
Duan Jiongc92a59e2013-08-22 12:07:35 +08002579 dst_release(dst);
2580}
2581
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002582void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
2583{
Lorenzo Colittie2d118a2016-11-04 02:23:43 +09002584 ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
2585 sk->sk_uid);
David S. Miller3a5ad2e2012-07-12 00:08:07 -07002586}
2587EXPORT_SYMBOL_GPL(ip6_sk_redirect);
2588
David S. Miller0dbaee32010-12-13 12:52:14 -08002589static unsigned int ip6_default_advmss(const struct dst_entry *dst)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590{
David S. Miller0dbaee32010-12-13 12:52:14 -08002591 struct net_device *dev = dst->dev;
2592 unsigned int mtu = dst_mtu(dst);
2593 struct net *net = dev_net(dev);
2594
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
2596
Daniel Lezcano55786892008-03-04 13:47:47 -08002597 if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
2598 mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599
2600 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002601 * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
2602 * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
2603 * IPV6_MAXPLEN is also valid and means: "any MSS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 * rely only on pmtu discovery"
2605 */
2606 if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
2607 mtu = IPV6_MAXPLEN;
2608 return mtu;
2609}
2610
Steffen Klassertebb762f2011-11-23 02:12:51 +00002611static unsigned int ip6_mtu(const struct dst_entry *dst)
David S. Millerd33e4552010-12-14 13:01:14 -08002612{
David S. Millerd33e4552010-12-14 13:01:14 -08002613 struct inet6_dev *idev;
David Ahernd4ead6b2018-04-17 17:33:16 -07002614 unsigned int mtu;
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002615
Martin KaFai Lau4b32b5a2015-04-28 13:03:06 -07002616 mtu = dst_metric_raw(dst, RTAX_MTU);
2617 if (mtu)
2618 goto out;
2619
Steffen Klassert618f9bc2011-11-23 02:13:31 +00002620 mtu = IPV6_MIN_MTU;
David S. Millerd33e4552010-12-14 13:01:14 -08002621
2622 rcu_read_lock();
2623 idev = __in6_dev_get(dst->dev);
2624 if (idev)
2625 mtu = idev->cnf.mtu6;
2626 rcu_read_unlock();
2627
Eric Dumazet30f78d82014-04-10 21:23:36 -07002628out:
Roopa Prabhu14972cb2016-08-24 20:10:43 -07002629 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2630
2631 return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
David S. Millerd33e4552010-12-14 13:01:14 -08002632}
2633
David Ahern901731b2018-05-21 09:08:14 -07002634/* MTU selection:
2635 * 1. mtu on route is locked - use it
2636 * 2. mtu from nexthop exception
2637 * 3. mtu from egress device
2638 *
2639 * based on ip6_dst_mtu_forward and exception logic of
2640 * rt6_find_cached_rt; called with rcu_read_lock
2641 */
2642u32 ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
2643 struct in6_addr *saddr)
2644{
2645 struct rt6_exception_bucket *bucket;
2646 struct rt6_exception *rt6_ex;
2647 struct in6_addr *src_key;
2648 struct inet6_dev *idev;
2649 u32 mtu = 0;
2650
2651 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
2652 mtu = f6i->fib6_pmtu;
2653 if (mtu)
2654 goto out;
2655 }
2656
2657 src_key = NULL;
2658#ifdef CONFIG_IPV6_SUBTREES
2659 if (f6i->fib6_src.plen)
2660 src_key = saddr;
2661#endif
2662
2663 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2664 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2665 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2666 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2667
2668 if (likely(!mtu)) {
2669 struct net_device *dev = fib6_info_nh_dev(f6i);
2670
2671 mtu = IPV6_MIN_MTU;
2672 idev = __in6_dev_get(dev);
2673 if (idev && idev->cnf.mtu6 > mtu)
2674 mtu = idev->cnf.mtu6;
2675 }
2676
2677 mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
2678out:
2679 return mtu - lwtunnel_headroom(fib6_info_nh_lwt(f6i), mtu);
2680}
2681
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -08002682struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
David S. Miller87a11572011-12-06 17:04:13 -05002683 struct flowi6 *fl6)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684{
David S. Miller87a11572011-12-06 17:04:13 -05002685 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 struct rt6_info *rt;
2687 struct inet6_dev *idev = in6_dev_get(dev);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09002688 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689
David S. Miller38308472011-12-03 18:02:47 -05002690 if (unlikely(!idev))
Eric Dumazet122bdf62012-03-14 21:13:11 +00002691 return ERR_PTR(-ENODEV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692
Martin KaFai Lauad706862015-08-14 11:05:52 -07002693 rt = ip6_dst_alloc(net, dev, 0);
David S. Miller38308472011-12-03 18:02:47 -05002694 if (unlikely(!rt)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 in6_dev_put(idev);
David S. Miller87a11572011-12-06 17:04:13 -05002696 dst = ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 goto out;
2698 }
2699
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002700 rt->dst.flags |= DST_HOST;
Brendan McGrath588753f2017-12-13 22:14:57 +11002701 rt->dst.input = ip6_input;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002702 rt->dst.output = ip6_output;
Julian Anastasov550bab42013-10-20 15:43:04 +03002703 rt->rt6i_gateway = fl6->daddr;
David S. Miller87a11572011-12-06 17:04:13 -05002704 rt->rt6i_dst.addr = fl6->daddr;
Yan, Zheng8e2ec632011-09-05 21:34:30 +00002705 rt->rt6i_dst.plen = 128;
2706 rt->rt6i_idev = idev;
Li RongQing14edd872012-10-24 14:01:18 +08002707 dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
Ido Schimmel4c981e22018-01-07 12:45:04 +02002709 /* Add this dst into uncached_list so that rt6_disable_ip() can
Wei Wang587fea72017-06-17 10:42:36 -07002710 * do proper release of the net_device
2711 */
2712 rt6_uncached_list_add(rt);
Wei Wang81eb8442017-10-06 12:06:11 -07002713 atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714
David S. Miller87a11572011-12-06 17:04:13 -05002715 dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0);
2716
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717out:
David S. Miller87a11572011-12-06 17:04:13 -05002718 return dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719}
2720
Daniel Lezcano569d3642008-01-18 03:56:57 -08002721static int ip6_dst_gc(struct dst_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722{
Alexey Dobriyan86393e52009-08-29 01:34:49 +00002723 struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
Daniel Lezcano7019b782008-03-04 13:50:14 -08002724 int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
2725 int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
2726 int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
2727 int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout;
2728 unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002729 int entries;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730
Eric Dumazetfc66f952010-10-08 06:37:34 +00002731 entries = dst_entries_get_fast(ops);
Michal Kubeček49a18d82013-08-01 10:04:24 +02002732 if (time_after(rt_last_gc + rt_min_interval, jiffies) &&
Eric Dumazetfc66f952010-10-08 06:37:34 +00002733 entries <= rt_max_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734 goto out;
2735
Benjamin Thery6891a342008-03-04 13:49:47 -08002736 net->ipv6.ip6_rt_gc_expire++;
Li RongQing14956642014-05-19 17:30:28 +08002737 fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
Eric Dumazetfc66f952010-10-08 06:37:34 +00002738 entries = dst_entries_get_slow(ops);
2739 if (entries < ops->gc_thresh)
Daniel Lezcano7019b782008-03-04 13:50:14 -08002740 net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741out:
Daniel Lezcano7019b782008-03-04 13:50:14 -08002742 net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity;
Eric Dumazetfc66f952010-10-08 06:37:34 +00002743 return entries > rt_max_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744}
2745
David Ahern8c145862016-04-24 21:26:04 -07002746static struct rt6_info *ip6_nh_lookup_table(struct net *net,
2747 struct fib6_config *cfg,
David Ahernf4797b32018-01-25 16:55:08 -08002748 const struct in6_addr *gw_addr,
2749 u32 tbid, int flags)
David Ahern8c145862016-04-24 21:26:04 -07002750{
2751 struct flowi6 fl6 = {
2752 .flowi6_oif = cfg->fc_ifindex,
2753 .daddr = *gw_addr,
2754 .saddr = cfg->fc_prefsrc,
2755 };
2756 struct fib6_table *table;
2757 struct rt6_info *rt;
David Ahern8c145862016-04-24 21:26:04 -07002758
David Ahernf4797b32018-01-25 16:55:08 -08002759 table = fib6_get_table(net, tbid);
David Ahern8c145862016-04-24 21:26:04 -07002760 if (!table)
2761 return NULL;
2762
2763 if (!ipv6_addr_any(&cfg->fc_prefsrc))
2764 flags |= RT6_LOOKUP_F_HAS_SADDR;
2765
David Ahernf4797b32018-01-25 16:55:08 -08002766 flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE;
David Ahernb75cc8f2018-03-02 08:32:17 -08002767 rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags);
David Ahern8c145862016-04-24 21:26:04 -07002768
2769 /* if table lookup failed, fall back to full lookup */
2770 if (rt == net->ipv6.ip6_null_entry) {
2771 ip6_rt_put(rt);
2772 rt = NULL;
2773 }
2774
2775 return rt;
2776}
2777
David Ahernfc1e64e2018-01-25 16:55:09 -08002778static int ip6_route_check_nh_onlink(struct net *net,
2779 struct fib6_config *cfg,
David Ahern9fbb7042018-03-13 08:29:36 -07002780 const struct net_device *dev,
David Ahernfc1e64e2018-01-25 16:55:09 -08002781 struct netlink_ext_ack *extack)
2782{
David Ahern44750f82018-02-06 13:17:06 -08002783 u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
David Ahernfc1e64e2018-01-25 16:55:09 -08002784 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2785 u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT;
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002786 struct fib6_info *from;
David Ahernfc1e64e2018-01-25 16:55:09 -08002787 struct rt6_info *grt;
2788 int err;
2789
2790 err = 0;
2791 grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0);
2792 if (grt) {
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002793 rcu_read_lock();
2794 from = rcu_dereference(grt->from);
David Ahern58e354c2018-02-06 12:14:12 -08002795 if (!grt->dst.error &&
David Ahern4ed591c2018-10-24 13:58:39 -07002796 /* ignore match if it is the default route */
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002797 from && !ipv6_addr_any(&from->fib6_dst.addr) &&
David Ahern58e354c2018-02-06 12:14:12 -08002798 (grt->rt6i_flags & flags || dev != grt->dst.dev)) {
David Ahern44750f82018-02-06 13:17:06 -08002799 NL_SET_ERR_MSG(extack,
2800 "Nexthop has invalid gateway or device mismatch");
David Ahernfc1e64e2018-01-25 16:55:09 -08002801 err = -EINVAL;
2802 }
Paolo Abenibf1dc8b2019-02-21 11:19:42 +01002803 rcu_read_unlock();
David Ahernfc1e64e2018-01-25 16:55:09 -08002804
2805 ip6_rt_put(grt);
2806 }
2807
2808 return err;
2809}
2810
David Ahern1edce992018-01-25 16:55:07 -08002811static int ip6_route_check_nh(struct net *net,
2812 struct fib6_config *cfg,
2813 struct net_device **_dev,
2814 struct inet6_dev **idev)
2815{
2816 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2817 struct net_device *dev = _dev ? *_dev : NULL;
2818 struct rt6_info *grt = NULL;
2819 int err = -EHOSTUNREACH;
2820
2821 if (cfg->fc_table) {
David Ahernf4797b32018-01-25 16:55:08 -08002822 int flags = RT6_LOOKUP_F_IFACE;
2823
2824 grt = ip6_nh_lookup_table(net, cfg, gw_addr,
2825 cfg->fc_table, flags);
David Ahern1edce992018-01-25 16:55:07 -08002826 if (grt) {
2827 if (grt->rt6i_flags & RTF_GATEWAY ||
2828 (dev && dev != grt->dst.dev)) {
2829 ip6_rt_put(grt);
2830 grt = NULL;
2831 }
2832 }
2833 }
2834
2835 if (!grt)
David Ahernb75cc8f2018-03-02 08:32:17 -08002836 grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1);
David Ahern1edce992018-01-25 16:55:07 -08002837
2838 if (!grt)
2839 goto out;
2840
2841 if (dev) {
2842 if (dev != grt->dst.dev) {
2843 ip6_rt_put(grt);
2844 goto out;
2845 }
2846 } else {
2847 *_dev = dev = grt->dst.dev;
2848 *idev = grt->rt6i_idev;
2849 dev_hold(dev);
2850 in6_dev_hold(grt->rt6i_idev);
2851 }
2852
2853 if (!(grt->rt6i_flags & RTF_GATEWAY))
2854 err = 0;
2855
2856 ip6_rt_put(grt);
2857
2858out:
2859 return err;
2860}
2861
David Ahern9fbb7042018-03-13 08:29:36 -07002862static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
2863 struct net_device **_dev, struct inet6_dev **idev,
2864 struct netlink_ext_ack *extack)
2865{
2866 const struct in6_addr *gw_addr = &cfg->fc_gateway;
2867 int gwa_type = ipv6_addr_type(gw_addr);
David Ahern232378e2018-03-13 08:29:37 -07002868 bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
David Ahern9fbb7042018-03-13 08:29:36 -07002869 const struct net_device *dev = *_dev;
David Ahern232378e2018-03-13 08:29:37 -07002870 bool need_addr_check = !dev;
David Ahern9fbb7042018-03-13 08:29:36 -07002871 int err = -EINVAL;
2872
2873 /* if gw_addr is local we will fail to detect this in case
2874 * address is still TENTATIVE (DAD in progress). rt6_lookup()
2875 * will return already-added prefix route via interface that
2876 * prefix route was assigned to, which might be non-loopback.
2877 */
David Ahern232378e2018-03-13 08:29:37 -07002878 if (dev &&
2879 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2880 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
David Ahern9fbb7042018-03-13 08:29:36 -07002881 goto out;
2882 }
2883
2884 if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
2885 /* IPv6 strictly inhibits using not link-local
2886 * addresses as nexthop address.
2887 * Otherwise, router will not able to send redirects.
2888 * It is very good, but in some (rare!) circumstances
2889 * (SIT, PtP, NBMA NOARP links) it is handy to allow
2890 * some exceptions. --ANK
2891 * We allow IPv4-mapped nexthops to support RFC4798-type
2892 * addressing
2893 */
2894 if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
2895 NL_SET_ERR_MSG(extack, "Invalid gateway address");
2896 goto out;
2897 }
2898
2899 if (cfg->fc_flags & RTNH_F_ONLINK)
2900 err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
2901 else
2902 err = ip6_route_check_nh(net, cfg, _dev, idev);
2903
2904 if (err)
2905 goto out;
2906 }
2907
2908 /* reload in case device was changed */
2909 dev = *_dev;
2910
2911 err = -EINVAL;
2912 if (!dev) {
2913 NL_SET_ERR_MSG(extack, "Egress device not specified");
2914 goto out;
2915 } else if (dev->flags & IFF_LOOPBACK) {
2916 NL_SET_ERR_MSG(extack,
2917 "Egress device can not be loopback device for this route");
2918 goto out;
2919 }
David Ahern232378e2018-03-13 08:29:37 -07002920
2921 /* if we did not check gw_addr above, do so now that the
2922 * egress device has been resolved.
2923 */
2924 if (need_addr_check &&
2925 ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
2926 NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
2927 goto out;
2928 }
2929
David Ahern9fbb7042018-03-13 08:29:36 -07002930 err = 0;
2931out:
2932 return err;
2933}
2934
David Ahern83c442512019-03-27 20:53:50 -07002935static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
2936{
2937 if ((flags & RTF_REJECT) ||
2938 (dev && (dev->flags & IFF_LOOPBACK) &&
2939 !(addr_type & IPV6_ADDR_LOOPBACK) &&
2940 !(flags & RTF_LOCAL)))
2941 return true;
2942
2943 return false;
2944}
2945
2946int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
2947 struct fib6_config *cfg, gfp_t gfp_flags,
2948 struct netlink_ext_ack *extack)
2949{
2950 struct net_device *dev = NULL;
2951 struct inet6_dev *idev = NULL;
2952 int addr_type;
2953 int err;
2954
David Ahernf1741732019-03-27 20:53:57 -07002955 fib6_nh->fib_nh_family = AF_INET6;
2956
David Ahern83c442512019-03-27 20:53:50 -07002957 err = -ENODEV;
2958 if (cfg->fc_ifindex) {
2959 dev = dev_get_by_index(net, cfg->fc_ifindex);
2960 if (!dev)
2961 goto out;
2962 idev = in6_dev_get(dev);
2963 if (!idev)
2964 goto out;
2965 }
2966
2967 if (cfg->fc_flags & RTNH_F_ONLINK) {
2968 if (!dev) {
2969 NL_SET_ERR_MSG(extack,
2970 "Nexthop device required for onlink");
2971 goto out;
2972 }
2973
2974 if (!(dev->flags & IFF_UP)) {
2975 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
2976 err = -ENETDOWN;
2977 goto out;
2978 }
2979
David Ahernad1601a2019-03-27 20:53:56 -07002980 fib6_nh->fib_nh_flags |= RTNH_F_ONLINK;
David Ahern83c442512019-03-27 20:53:50 -07002981 }
2982
David Ahernad1601a2019-03-27 20:53:56 -07002983 fib6_nh->fib_nh_weight = 1;
David Ahern83c442512019-03-27 20:53:50 -07002984
2985 /* We cannot add true routes via loopback here,
2986 * they would result in kernel looping; promote them to reject routes
2987 */
2988 addr_type = ipv6_addr_type(&cfg->fc_dst);
2989 if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) {
2990 /* hold loopback dev/idev if we haven't done so. */
2991 if (dev != net->loopback_dev) {
2992 if (dev) {
2993 dev_put(dev);
2994 in6_dev_put(idev);
2995 }
2996 dev = net->loopback_dev;
2997 dev_hold(dev);
2998 idev = in6_dev_get(dev);
2999 if (!idev) {
3000 err = -ENODEV;
3001 goto out;
3002 }
3003 }
3004 goto set_dev;
3005 }
3006
3007 if (cfg->fc_flags & RTF_GATEWAY) {
3008 err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
3009 if (err)
3010 goto out;
3011
David Ahernad1601a2019-03-27 20:53:56 -07003012 fib6_nh->fib_nh_gw6 = cfg->fc_gateway;
David Ahernbdf00462019-04-05 16:30:26 -07003013 fib6_nh->fib_nh_gw_family = AF_INET6;
David Ahern83c442512019-03-27 20:53:50 -07003014 }
3015
3016 err = -ENODEV;
3017 if (!dev)
3018 goto out;
3019
3020 if (idev->cnf.disable_ipv6) {
3021 NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device");
3022 err = -EACCES;
3023 goto out;
3024 }
3025
3026 if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) {
3027 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
3028 err = -ENETDOWN;
3029 goto out;
3030 }
3031
3032 if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
3033 !netif_carrier_ok(dev))
David Ahernad1601a2019-03-27 20:53:56 -07003034 fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
David Ahern83c442512019-03-27 20:53:50 -07003035
David Ahern979e2762019-03-27 20:53:58 -07003036 err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
3037 cfg->fc_encap_type, cfg, gfp_flags, extack);
3038 if (err)
3039 goto out;
David Ahern83c442512019-03-27 20:53:50 -07003040set_dev:
David Ahernad1601a2019-03-27 20:53:56 -07003041 fib6_nh->fib_nh_dev = dev;
David Ahernf1741732019-03-27 20:53:57 -07003042 fib6_nh->fib_nh_oif = dev->ifindex;
David Ahern83c442512019-03-27 20:53:50 -07003043 err = 0;
3044out:
3045 if (idev)
3046 in6_dev_put(idev);
3047
3048 if (err) {
David Ahernad1601a2019-03-27 20:53:56 -07003049 lwtstate_put(fib6_nh->fib_nh_lws);
3050 fib6_nh->fib_nh_lws = NULL;
David Ahern83c442512019-03-27 20:53:50 -07003051 if (dev)
3052 dev_put(dev);
3053 }
3054
3055 return err;
3056}
3057
David Aherndac7d0f2019-03-27 20:53:51 -07003058void fib6_nh_release(struct fib6_nh *fib6_nh)
3059{
David Ahern979e2762019-03-27 20:53:58 -07003060 fib_nh_common_release(&fib6_nh->nh_common);
David Aherndac7d0f2019-03-27 20:53:51 -07003061}
3062
David Ahern8d1c8022018-04-17 17:33:26 -07003063static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
David Ahernacb54e32018-04-17 17:33:22 -07003064 gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003065 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066{
Daniel Lezcano55786892008-03-04 13:47:47 -08003067 struct net *net = cfg->fc_nlinfo.nl_net;
David Ahern8d1c8022018-04-17 17:33:26 -07003068 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003069 struct fib6_table *table;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003070 int err = -EINVAL;
David Ahern83c442512019-03-27 20:53:50 -07003071 int addr_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072
David Ahern557c44b2017-04-19 14:19:43 -07003073 /* RTF_PCPU is an internal flag; can not be set by userspace */
David Ahernd5d531c2017-05-21 10:12:05 -06003074 if (cfg->fc_flags & RTF_PCPU) {
3075 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU");
David Ahern557c44b2017-04-19 14:19:43 -07003076 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003077 }
David Ahern557c44b2017-04-19 14:19:43 -07003078
Wei Wang2ea23522017-10-27 17:30:12 -07003079 /* RTF_CACHE is an internal flag; can not be set by userspace */
3080 if (cfg->fc_flags & RTF_CACHE) {
3081 NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE");
3082 goto out;
3083 }
3084
David Aherne8478e82018-04-17 17:33:13 -07003085 if (cfg->fc_type > RTN_MAX) {
3086 NL_SET_ERR_MSG(extack, "Invalid route type");
3087 goto out;
3088 }
3089
David Ahernd5d531c2017-05-21 10:12:05 -06003090 if (cfg->fc_dst_len > 128) {
3091 NL_SET_ERR_MSG(extack, "Invalid prefix length");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003092 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003093 }
3094 if (cfg->fc_src_len > 128) {
3095 NL_SET_ERR_MSG(extack, "Invalid source address length");
3096 goto out;
3097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098#ifndef CONFIG_IPV6_SUBTREES
David Ahernd5d531c2017-05-21 10:12:05 -06003099 if (cfg->fc_src_len) {
3100 NL_SET_ERR_MSG(extack,
3101 "Specifying source address requires IPV6_SUBTREES to be enabled");
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003102 goto out;
David Ahernd5d531c2017-05-21 10:12:05 -06003103 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104#endif
David Ahernfc1e64e2018-01-25 16:55:09 -08003105
Matti Vaittinend71314b2011-11-14 00:14:49 +00003106 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05003107 if (cfg->fc_nlinfo.nlh &&
3108 !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) {
Matti Vaittinend71314b2011-11-14 00:14:49 +00003109 table = fib6_get_table(net, cfg->fc_table);
David S. Miller38308472011-12-03 18:02:47 -05003110 if (!table) {
Joe Perchesf3213832012-05-15 14:11:53 +00003111 pr_warn("NLM_F_CREATE should be specified when creating new route\n");
Matti Vaittinend71314b2011-11-14 00:14:49 +00003112 table = fib6_new_table(net, cfg->fc_table);
3113 }
3114 } else {
3115 table = fib6_new_table(net, cfg->fc_table);
3116 }
David S. Miller38308472011-12-03 18:02:47 -05003117
3118 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003119 goto out;
Thomas Grafc71099a2006-08-04 23:20:06 -07003120
David Ahern93531c62018-04-17 17:33:25 -07003121 err = -ENOMEM;
3122 rt = fib6_info_alloc(gfp_flags);
3123 if (!rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 goto out;
David Ahern93531c62018-04-17 17:33:25 -07003125
David Ahernd7e774f2018-11-06 12:51:15 -08003126 rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
3127 extack);
David Ahern767a2212018-10-04 20:07:51 -07003128 if (IS_ERR(rt->fib6_metrics)) {
3129 err = PTR_ERR(rt->fib6_metrics);
Eric Dumazetfda21d42018-10-05 09:17:50 -07003130 /* Do not leave garbage there. */
3131 rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics;
David Ahern767a2212018-10-04 20:07:51 -07003132 goto out;
3133 }
3134
David Ahern93531c62018-04-17 17:33:25 -07003135 if (cfg->fc_flags & RTF_ADDRCONF)
3136 rt->dst_nocount = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003137
Gao feng1716a962012-04-06 00:13:10 +00003138 if (cfg->fc_flags & RTF_EXPIRES)
David Ahern14895682018-04-17 17:33:17 -07003139 fib6_set_expires(rt, jiffies +
Gao feng1716a962012-04-06 00:13:10 +00003140 clock_t_to_jiffies(cfg->fc_expires));
3141 else
David Ahern14895682018-04-17 17:33:17 -07003142 fib6_clean_expires(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003143
Thomas Graf86872cb2006-08-22 00:01:08 -07003144 if (cfg->fc_protocol == RTPROT_UNSPEC)
3145 cfg->fc_protocol = RTPROT_BOOT;
David Ahern93c2fb22018-04-18 15:38:59 -07003146 rt->fib6_protocol = cfg->fc_protocol;
Thomas Graf86872cb2006-08-22 00:01:08 -07003147
David Ahern83c442512019-03-27 20:53:50 -07003148 rt->fib6_table = table;
3149 rt->fib6_metric = cfg->fc_metric;
3150 rt->fib6_type = cfg->fc_type;
David Ahern2b2450c2019-03-27 20:53:52 -07003151 rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY;
Roopa Prabhu19e42e42015-07-21 10:43:48 +02003152
David Ahern93c2fb22018-04-18 15:38:59 -07003153 ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len);
3154 rt->fib6_dst.plen = cfg->fc_dst_len;
3155 if (rt->fib6_dst.plen == 128)
David Ahern3b6761d2018-04-17 17:33:20 -07003156 rt->dst_host = true;
Michal Kubečeke5fd3872014-03-27 13:04:08 +01003157
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158#ifdef CONFIG_IPV6_SUBTREES
David Ahern93c2fb22018-04-18 15:38:59 -07003159 ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len);
3160 rt->fib6_src.plen = cfg->fc_src_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003161#endif
David Ahern83c442512019-03-27 20:53:50 -07003162 err = fib6_nh_init(net, &rt->fib6_nh, cfg, gfp_flags, extack);
3163 if (err)
3164 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165
3166 /* We cannot add true routes via loopback here,
David Ahern83c442512019-03-27 20:53:50 -07003167 * they would result in kernel looping; promote them to reject routes
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168 */
David Ahern83c442512019-03-27 20:53:50 -07003169 addr_type = ipv6_addr_type(&cfg->fc_dst);
David Ahernad1601a2019-03-27 20:53:56 -07003170 if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh.fib_nh_dev, addr_type))
David Ahern83c442512019-03-27 20:53:50 -07003171 rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP;
David Ahern955ec4c2018-01-24 19:45:29 -08003172
Daniel Walterc3968a82011-04-13 21:10:57 +00003173 if (!ipv6_addr_any(&cfg->fc_prefsrc)) {
David Ahern83c442512019-03-27 20:53:50 -07003174 struct net_device *dev = fib6_info_nh_dev(rt);
3175
Daniel Walterc3968a82011-04-13 21:10:57 +00003176 if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) {
David Ahernd5d531c2017-05-21 10:12:05 -06003177 NL_SET_ERR_MSG(extack, "Invalid source address");
Daniel Walterc3968a82011-04-13 21:10:57 +00003178 err = -EINVAL;
3179 goto out;
3180 }
David Ahern93c2fb22018-04-18 15:38:59 -07003181 rt->fib6_prefsrc.addr = cfg->fc_prefsrc;
3182 rt->fib6_prefsrc.plen = 128;
Daniel Walterc3968a82011-04-13 21:10:57 +00003183 } else
David Ahern93c2fb22018-04-18 15:38:59 -07003184 rt->fib6_prefsrc.plen = 0;
Daniel Walterc3968a82011-04-13 21:10:57 +00003185
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003186 return rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187out:
David Ahern93531c62018-04-17 17:33:25 -07003188 fib6_info_release(rt);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07003189 return ERR_PTR(err);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003190}
3191
David Ahernacb54e32018-04-17 17:33:22 -07003192int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
David Ahern333c4302017-05-21 10:12:04 -06003193 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003194{
David Ahern8d1c8022018-04-17 17:33:26 -07003195 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003196 int err;
3197
David Ahernacb54e32018-04-17 17:33:22 -07003198 rt = ip6_route_info_create(cfg, gfp_flags, extack);
David Ahernd4ead6b2018-04-17 17:33:16 -07003199 if (IS_ERR(rt))
3200 return PTR_ERR(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003201
David Ahernd4ead6b2018-04-17 17:33:16 -07003202 err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
David Ahern93531c62018-04-17 17:33:25 -07003203 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07003204
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205 return err;
3206}
3207
David Ahern8d1c8022018-04-17 17:33:26 -07003208static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209{
David Ahernafb1d4b52018-04-17 17:33:11 -07003210 struct net *net = info->nl_net;
Thomas Grafc71099a2006-08-04 23:20:06 -07003211 struct fib6_table *table;
David Ahernafb1d4b52018-04-17 17:33:11 -07003212 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213
David Ahern421842e2018-04-17 17:33:18 -07003214 if (rt == net->ipv6.fib6_null_entry) {
Gao feng6825a262012-09-19 19:25:34 +00003215 err = -ENOENT;
3216 goto out;
3217 }
Patrick McHardy6c813a72006-08-06 22:22:47 -07003218
David Ahern93c2fb22018-04-18 15:38:59 -07003219 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003220 spin_lock_bh(&table->tb6_lock);
Thomas Graf86872cb2006-08-22 00:01:08 -07003221 err = fib6_del(rt, info);
Wei Wang66f5d6c2017-10-06 12:06:10 -07003222 spin_unlock_bh(&table->tb6_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223
Gao feng6825a262012-09-19 19:25:34 +00003224out:
David Ahern93531c62018-04-17 17:33:25 -07003225 fib6_info_release(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 return err;
3227}
3228
David Ahern8d1c8022018-04-17 17:33:26 -07003229int ip6_del_rt(struct net *net, struct fib6_info *rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003230{
David Ahernafb1d4b52018-04-17 17:33:11 -07003231 struct nl_info info = { .nl_net = net };
3232
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08003233 return __ip6_del_rt(rt, &info);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07003234}
3235
David Ahern8d1c8022018-04-17 17:33:26 -07003236static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
David Ahern0ae81332017-02-02 12:37:08 -08003237{
3238 struct nl_info *info = &cfg->fc_nlinfo;
WANG Conge3330032017-02-27 16:07:43 -08003239 struct net *net = info->nl_net;
David Ahern16a16cd2017-02-02 12:37:11 -08003240 struct sk_buff *skb = NULL;
David Ahern0ae81332017-02-02 12:37:08 -08003241 struct fib6_table *table;
WANG Conge3330032017-02-27 16:07:43 -08003242 int err = -ENOENT;
David Ahern0ae81332017-02-02 12:37:08 -08003243
David Ahern421842e2018-04-17 17:33:18 -07003244 if (rt == net->ipv6.fib6_null_entry)
WANG Conge3330032017-02-27 16:07:43 -08003245 goto out_put;
David Ahern93c2fb22018-04-18 15:38:59 -07003246 table = rt->fib6_table;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003247 spin_lock_bh(&table->tb6_lock);
David Ahern0ae81332017-02-02 12:37:08 -08003248
David Ahern93c2fb22018-04-18 15:38:59 -07003249 if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) {
David Ahern8d1c8022018-04-17 17:33:26 -07003250 struct fib6_info *sibling, *next_sibling;
David Ahern0ae81332017-02-02 12:37:08 -08003251
David Ahern16a16cd2017-02-02 12:37:11 -08003252 /* prefer to send a single notification with all hops */
3253 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
3254 if (skb) {
3255 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
3256
David Ahernd4ead6b2018-04-17 17:33:16 -07003257 if (rt6_fill_node(net, skb, rt, NULL,
David Ahern16a16cd2017-02-02 12:37:11 -08003258 NULL, NULL, 0, RTM_DELROUTE,
3259 info->portid, seq, 0) < 0) {
3260 kfree_skb(skb);
3261 skb = NULL;
3262 } else
3263 info->skip_notify = 1;
3264 }
3265
David Ahern0ae81332017-02-02 12:37:08 -08003266 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07003267 &rt->fib6_siblings,
3268 fib6_siblings) {
David Ahern0ae81332017-02-02 12:37:08 -08003269 err = fib6_del(sibling, info);
3270 if (err)
WANG Conge3330032017-02-27 16:07:43 -08003271 goto out_unlock;
David Ahern0ae81332017-02-02 12:37:08 -08003272 }
3273 }
3274
3275 err = fib6_del(rt, info);
WANG Conge3330032017-02-27 16:07:43 -08003276out_unlock:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003277 spin_unlock_bh(&table->tb6_lock);
WANG Conge3330032017-02-27 16:07:43 -08003278out_put:
David Ahern93531c62018-04-17 17:33:25 -07003279 fib6_info_release(rt);
David Ahern16a16cd2017-02-02 12:37:11 -08003280
3281 if (skb) {
WANG Conge3330032017-02-27 16:07:43 -08003282 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
David Ahern16a16cd2017-02-02 12:37:11 -08003283 info->nlh, gfp_any());
3284 }
David Ahern0ae81332017-02-02 12:37:08 -08003285 return err;
3286}
3287
David Ahern23fb93a2018-04-17 17:33:23 -07003288static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg)
3289{
3290 int rc = -ESRCH;
3291
3292 if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex)
3293 goto out;
3294
3295 if (cfg->fc_flags & RTF_GATEWAY &&
3296 !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
3297 goto out;
Xin Long761f6022018-11-14 00:48:28 +08003298
3299 rc = rt6_remove_exception_rt(rt);
David Ahern23fb93a2018-04-17 17:33:23 -07003300out:
3301 return rc;
3302}
3303
David Ahern333c4302017-05-21 10:12:04 -06003304static int ip6_route_del(struct fib6_config *cfg,
3305 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306{
David Ahern8d1c8022018-04-17 17:33:26 -07003307 struct rt6_info *rt_cache;
Thomas Grafc71099a2006-08-04 23:20:06 -07003308 struct fib6_table *table;
David Ahern8d1c8022018-04-17 17:33:26 -07003309 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003310 struct fib6_node *fn;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003311 int err = -ESRCH;
3312
Daniel Lezcano55786892008-03-04 13:47:47 -08003313 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
David Ahernd5d531c2017-05-21 10:12:05 -06003314 if (!table) {
3315 NL_SET_ERR_MSG(extack, "FIB table does not exist");
Thomas Grafc71099a2006-08-04 23:20:06 -07003316 return err;
David Ahernd5d531c2017-05-21 10:12:05 -06003317 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003318
Wei Wang66f5d6c2017-10-06 12:06:10 -07003319 rcu_read_lock();
Thomas Grafc71099a2006-08-04 23:20:06 -07003320
3321 fn = fib6_locate(&table->tb6_root,
Thomas Graf86872cb2006-08-22 00:01:08 -07003322 &cfg->fc_dst, cfg->fc_dst_len,
Wei Wang38fbeee2017-10-06 12:06:02 -07003323 &cfg->fc_src, cfg->fc_src_len,
Wei Wang2b760fc2017-10-06 12:06:03 -07003324 !(cfg->fc_flags & RTF_CACHE));
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003325
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326 if (fn) {
Wei Wang66f5d6c2017-10-06 12:06:10 -07003327 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003328 struct fib6_nh *nh;
3329
Wei Wang2b760fc2017-10-06 12:06:03 -07003330 if (cfg->fc_flags & RTF_CACHE) {
David Ahern7e4b5122019-04-16 14:36:00 -07003331 struct fib6_result res = {
3332 .f6i = rt,
3333 };
David Ahern23fb93a2018-04-17 17:33:23 -07003334 int rc;
3335
David Ahern7e4b5122019-04-16 14:36:00 -07003336 rt_cache = rt6_find_cached_rt(&res,
3337 &cfg->fc_dst,
Wei Wang2b760fc2017-10-06 12:06:03 -07003338 &cfg->fc_src);
David Ahern23fb93a2018-04-17 17:33:23 -07003339 if (rt_cache) {
3340 rc = ip6_del_cached_rt(rt_cache, cfg);
Eric Dumazet9e575012018-05-09 10:05:46 -07003341 if (rc != -ESRCH) {
3342 rcu_read_unlock();
David Ahern23fb93a2018-04-17 17:33:23 -07003343 return rc;
Eric Dumazet9e575012018-05-09 10:05:46 -07003344 }
David Ahern23fb93a2018-04-17 17:33:23 -07003345 }
3346 continue;
Wei Wang2b760fc2017-10-06 12:06:03 -07003347 }
David Ahernad1601a2019-03-27 20:53:56 -07003348
3349 nh = &rt->fib6_nh;
Thomas Graf86872cb2006-08-22 00:01:08 -07003350 if (cfg->fc_ifindex &&
David Ahernad1601a2019-03-27 20:53:56 -07003351 (!nh->fib_nh_dev ||
3352 nh->fib_nh_dev->ifindex != cfg->fc_ifindex))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353 continue;
Thomas Graf86872cb2006-08-22 00:01:08 -07003354 if (cfg->fc_flags & RTF_GATEWAY &&
David Ahernad1601a2019-03-27 20:53:56 -07003355 !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003357 if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358 continue;
David Ahern93c2fb22018-04-18 15:38:59 -07003359 if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol)
Mantas Mc2ed1882016-12-16 10:30:59 +02003360 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003361 if (!fib6_info_hold_safe(rt))
3362 continue;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003363 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003364
David Ahern0ae81332017-02-02 12:37:08 -08003365 /* if gateway was specified only delete the one hop */
3366 if (cfg->fc_flags & RTF_GATEWAY)
3367 return __ip6_del_rt(rt, &cfg->fc_nlinfo);
3368
3369 return __ip6_del_rt_siblings(rt, cfg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003370 }
3371 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003372 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003373
3374 return err;
3375}
3376
David S. Miller6700c272012-07-17 03:29:28 -07003377static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003378{
YOSHIFUJI Hideakia6279452006-08-23 17:18:26 -07003379 struct netevent_redirect netevent;
David S. Millere8599ff2012-07-11 23:43:53 -07003380 struct rt6_info *rt, *nrt = NULL;
David Ahern85bd05d2019-04-16 14:36:01 -07003381 struct fib6_result res = {};
David S. Millere8599ff2012-07-11 23:43:53 -07003382 struct ndisc_options ndopts;
3383 struct inet6_dev *in6_dev;
3384 struct neighbour *neigh;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003385 struct rd_msg *msg;
David S. Miller6e157b62012-07-12 00:05:02 -07003386 int optlen, on_link;
3387 u8 *lladdr;
David S. Millere8599ff2012-07-11 23:43:53 -07003388
Simon Horman29a3cad2013-05-28 20:34:26 +00003389 optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003390 optlen -= sizeof(*msg);
David S. Millere8599ff2012-07-11 23:43:53 -07003391
3392 if (optlen < 0) {
David S. Miller6e157b62012-07-12 00:05:02 -07003393 net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003394 return;
3395 }
3396
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003397 msg = (struct rd_msg *)icmp6_hdr(skb);
David S. Millere8599ff2012-07-11 23:43:53 -07003398
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003399 if (ipv6_addr_is_multicast(&msg->dest)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003400 net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003401 return;
3402 }
3403
David S. Miller6e157b62012-07-12 00:05:02 -07003404 on_link = 0;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003405 if (ipv6_addr_equal(&msg->dest, &msg->target)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003406 on_link = 1;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003407 } else if (ipv6_addr_type(&msg->target) !=
David S. Millere8599ff2012-07-11 23:43:53 -07003408 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
David S. Miller6e157b62012-07-12 00:05:02 -07003409 net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
David S. Millere8599ff2012-07-11 23:43:53 -07003410 return;
3411 }
3412
3413 in6_dev = __in6_dev_get(skb->dev);
3414 if (!in6_dev)
3415 return;
3416 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
3417 return;
3418
3419 /* RFC2461 8.1:
3420 * The IP source address of the Redirect MUST be the same as the current
3421 * first-hop router for the specified ICMP Destination Address.
3422 */
3423
Alexander Aringf997c552016-06-15 21:20:23 +02003424 if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
David S. Millere8599ff2012-07-11 23:43:53 -07003425 net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
3426 return;
3427 }
David S. Miller6e157b62012-07-12 00:05:02 -07003428
3429 lladdr = NULL;
David S. Millere8599ff2012-07-11 23:43:53 -07003430 if (ndopts.nd_opts_tgt_lladdr) {
3431 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
3432 skb->dev);
3433 if (!lladdr) {
3434 net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
3435 return;
3436 }
3437 }
3438
David S. Miller6e157b62012-07-12 00:05:02 -07003439 rt = (struct rt6_info *) dst;
Matthias Schifferec13ad12015-11-02 01:24:38 +01003440 if (rt->rt6i_flags & RTF_REJECT) {
David S. Miller6e157b62012-07-12 00:05:02 -07003441 net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
3442 return;
3443 }
3444
3445 /* Redirect received -> path was valid.
3446 * Look, redirects are sent only in response to data packets,
3447 * so that this nexthop apparently is reachable. --ANK
3448 */
Julian Anastasov0dec8792017-02-06 23:14:16 +02003449 dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
David S. Miller6e157b62012-07-12 00:05:02 -07003450
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003451 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
David S. Millere8599ff2012-07-11 23:43:53 -07003452 if (!neigh)
3453 return;
3454
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455 /*
3456 * We have finally decided to accept it.
3457 */
3458
Alexander Aringf997c552016-06-15 21:20:23 +02003459 ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003460 NEIGH_UPDATE_F_WEAK_OVERRIDE|
3461 NEIGH_UPDATE_F_OVERRIDE|
3462 (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
Alexander Aringf997c552016-06-15 21:20:23 +02003463 NEIGH_UPDATE_F_ISROUTER)),
3464 NDISC_REDIRECT, &ndopts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003465
David Ahern4d85cd02018-04-20 15:37:59 -07003466 rcu_read_lock();
David Ahern85bd05d2019-04-16 14:36:01 -07003467 res.f6i = rcu_dereference(rt->from);
Wei Wange873e4b2018-07-21 20:56:32 -07003468 /* This fib6_info_hold() is safe here because we hold reference to rt
3469 * and rt already holds reference to fib6_info.
3470 */
David Ahern85bd05d2019-04-16 14:36:01 -07003471 fib6_info_hold(res.f6i);
David Ahern4d85cd02018-04-20 15:37:59 -07003472 rcu_read_unlock();
David Ahern8a14e462018-04-23 11:32:07 -07003473
David Ahern85bd05d2019-04-16 14:36:01 -07003474 res.nh = &res.f6i->fib6_nh;
3475 nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL);
David S. Miller38308472011-12-03 18:02:47 -05003476 if (!nrt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477 goto out;
3478
3479 nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
3480 if (on_link)
3481 nrt->rt6i_flags &= ~RTF_GATEWAY;
3482
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003483 nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484
Wei Wang2b760fc2017-10-06 12:06:03 -07003485 /* No need to remove rt from the exception table if rt is
3486 * a cached route because rt6_insert_exception() will
3487 * takes care of it
3488 */
David Ahern85bd05d2019-04-16 14:36:01 -07003489 if (rt6_insert_exception(nrt, res.f6i)) {
Wei Wang2b760fc2017-10-06 12:06:03 -07003490 dst_release_immediate(&nrt->dst);
3491 goto out;
3492 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003493
Changli Gaod8d1f302010-06-10 23:31:35 -07003494 netevent.old = &rt->dst;
3495 netevent.new = &nrt->dst;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00003496 netevent.daddr = &msg->dest;
YOSHIFUJI Hideaki / 吉藤英明60592832013-01-14 09:28:27 +00003497 netevent.neigh = neigh;
Tom Tucker8d717402006-07-30 20:43:36 -07003498 call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
3499
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500out:
David Ahern85bd05d2019-04-16 14:36:01 -07003501 fib6_info_release(res.f6i);
David S. Millere8599ff2012-07-11 23:43:53 -07003502 neigh_release(neigh);
David S. Miller6e157b62012-07-12 00:05:02 -07003503}
3504
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003505#ifdef CONFIG_IPV6_ROUTE_INFO
David Ahern8d1c8022018-04-17 17:33:26 -07003506static struct fib6_info *rt6_get_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003507 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003508 const struct in6_addr *gwaddr,
3509 struct net_device *dev)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003510{
David Ahern830218c2016-10-24 10:52:35 -07003511 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO;
3512 int ifindex = dev->ifindex;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003513 struct fib6_node *fn;
David Ahern8d1c8022018-04-17 17:33:26 -07003514 struct fib6_info *rt = NULL;
Thomas Grafc71099a2006-08-04 23:20:06 -07003515 struct fib6_table *table;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003516
David Ahern830218c2016-10-24 10:52:35 -07003517 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003518 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003519 return NULL;
3520
Wei Wang66f5d6c2017-10-06 12:06:10 -07003521 rcu_read_lock();
Wei Wang38fbeee2017-10-06 12:06:02 -07003522 fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003523 if (!fn)
3524 goto out;
3525
Wei Wang66f5d6c2017-10-06 12:06:10 -07003526 for_each_fib6_node_rt_rcu(fn) {
David Ahernad1601a2019-03-27 20:53:56 -07003527 if (rt->fib6_nh.fib_nh_dev->ifindex != ifindex)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003528 continue;
David Ahern2b2450c2019-03-27 20:53:52 -07003529 if (!(rt->fib6_flags & RTF_ROUTEINFO) ||
David Ahernbdf00462019-04-05 16:30:26 -07003530 !rt->fib6_nh.fib_nh_gw_family)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003531 continue;
David Ahernad1601a2019-03-27 20:53:56 -07003532 if (!ipv6_addr_equal(&rt->fib6_nh.fib_nh_gw6, gwaddr))
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003533 continue;
Wei Wange873e4b2018-07-21 20:56:32 -07003534 if (!fib6_info_hold_safe(rt))
3535 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003536 break;
3537 }
3538out:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003539 rcu_read_unlock();
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003540 return rt;
3541}
3542
David Ahern8d1c8022018-04-17 17:33:26 -07003543static struct fib6_info *rt6_add_route_info(struct net *net,
Eric Dumazetb71d1d42011-04-22 04:53:02 +00003544 const struct in6_addr *prefix, int prefixlen,
David Ahern830218c2016-10-24 10:52:35 -07003545 const struct in6_addr *gwaddr,
3546 struct net_device *dev,
Eric Dumazet95c96172012-04-15 05:58:06 +00003547 unsigned int pref)
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003548{
Thomas Graf86872cb2006-08-22 00:01:08 -07003549 struct fib6_config cfg = {
Rami Rosen238fc7e2008-02-09 23:43:11 -08003550 .fc_metric = IP6_RT_PRIO_USER,
David Ahern830218c2016-10-24 10:52:35 -07003551 .fc_ifindex = dev->ifindex,
Thomas Graf86872cb2006-08-22 00:01:08 -07003552 .fc_dst_len = prefixlen,
3553 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO |
3554 RTF_UP | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003555 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003556 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003557 .fc_nlinfo.portid = 0,
Daniel Lezcanoefa2cea2008-03-04 13:46:48 -08003558 .fc_nlinfo.nlh = NULL,
3559 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003560 };
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003561
David Ahern830218c2016-10-24 10:52:35 -07003562 cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO,
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003563 cfg.fc_dst = *prefix;
3564 cfg.fc_gateway = *gwaddr;
Thomas Graf86872cb2006-08-22 00:01:08 -07003565
YOSHIFUJI Hideakie317da92006-03-20 17:06:42 -08003566 /* We should treat it as a default route if prefix length is 0. */
3567 if (!prefixlen)
Thomas Graf86872cb2006-08-22 00:01:08 -07003568 cfg.fc_flags |= RTF_DEFAULT;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003569
David Ahernacb54e32018-04-17 17:33:22 -07003570 ip6_route_add(&cfg, GFP_ATOMIC, NULL);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003571
David Ahern830218c2016-10-24 10:52:35 -07003572 return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08003573}
3574#endif
3575
David Ahern8d1c8022018-04-17 17:33:26 -07003576struct fib6_info *rt6_get_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003577 const struct in6_addr *addr,
3578 struct net_device *dev)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003579{
David Ahern830218c2016-10-24 10:52:35 -07003580 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT;
David Ahern8d1c8022018-04-17 17:33:26 -07003581 struct fib6_info *rt;
Thomas Grafc71099a2006-08-04 23:20:06 -07003582 struct fib6_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003583
David Ahernafb1d4b52018-04-17 17:33:11 -07003584 table = fib6_get_table(net, tb_id);
David S. Miller38308472011-12-03 18:02:47 -05003585 if (!table)
Thomas Grafc71099a2006-08-04 23:20:06 -07003586 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003587
Wei Wang66f5d6c2017-10-06 12:06:10 -07003588 rcu_read_lock();
3589 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Ahernad1601a2019-03-27 20:53:56 -07003590 struct fib6_nh *nh = &rt->fib6_nh;
3591
3592 if (dev == nh->fib_nh_dev &&
David Ahern93c2fb22018-04-18 15:38:59 -07003593 ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
David Ahernad1601a2019-03-27 20:53:56 -07003594 ipv6_addr_equal(&nh->fib_nh_gw6, addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595 break;
3596 }
Wei Wange873e4b2018-07-21 20:56:32 -07003597 if (rt && !fib6_info_hold_safe(rt))
3598 rt = NULL;
Wei Wang66f5d6c2017-10-06 12:06:10 -07003599 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 return rt;
3601}
3602
David Ahern8d1c8022018-04-17 17:33:26 -07003603struct fib6_info *rt6_add_dflt_router(struct net *net,
David Ahernafb1d4b52018-04-17 17:33:11 -07003604 const struct in6_addr *gwaddr,
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08003605 struct net_device *dev,
3606 unsigned int pref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003607{
Thomas Graf86872cb2006-08-22 00:01:08 -07003608 struct fib6_config cfg = {
David Ahernca254492015-10-12 11:47:10 -07003609 .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
Rami Rosen238fc7e2008-02-09 23:43:11 -08003610 .fc_metric = IP6_RT_PRIO_USER,
Thomas Graf86872cb2006-08-22 00:01:08 -07003611 .fc_ifindex = dev->ifindex,
3612 .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
3613 RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
Xin Longb91d5322017-08-03 14:13:46 +08003614 .fc_protocol = RTPROT_RA,
David Aherne8478e82018-04-17 17:33:13 -07003615 .fc_type = RTN_UNICAST,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003616 .fc_nlinfo.portid = 0,
Daniel Lezcano55786892008-03-04 13:47:47 -08003617 .fc_nlinfo.nlh = NULL,
David Ahernafb1d4b52018-04-17 17:33:11 -07003618 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003619 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00003621 cfg.fc_gateway = *gwaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003622
David Ahernacb54e32018-04-17 17:33:22 -07003623 if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) {
David Ahern830218c2016-10-24 10:52:35 -07003624 struct fib6_table *table;
3625
3626 table = fib6_get_table(dev_net(dev), cfg.fc_table);
3627 if (table)
3628 table->flags |= RT6_TABLE_HAS_DFLT_ROUTER;
3629 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630
David Ahernafb1d4b52018-04-17 17:33:11 -07003631 return rt6_get_dflt_router(net, gwaddr, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632}
3633
David Ahernafb1d4b52018-04-17 17:33:11 -07003634static void __rt6_purge_dflt_routers(struct net *net,
3635 struct fib6_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636{
David Ahern8d1c8022018-04-17 17:33:26 -07003637 struct fib6_info *rt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638
3639restart:
Wei Wang66f5d6c2017-10-06 12:06:10 -07003640 rcu_read_lock();
3641 for_each_fib6_node_rt_rcu(&table->tb6_root) {
David Aherndcd1f572018-04-18 15:39:05 -07003642 struct net_device *dev = fib6_info_nh_dev(rt);
3643 struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL;
3644
David Ahern93c2fb22018-04-18 15:38:59 -07003645 if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) &&
Wei Wange873e4b2018-07-21 20:56:32 -07003646 (!idev || idev->cnf.accept_ra != 2) &&
3647 fib6_info_hold_safe(rt)) {
David Ahern93531c62018-04-17 17:33:25 -07003648 rcu_read_unlock();
3649 ip6_del_rt(net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650 goto restart;
3651 }
3652 }
Wei Wang66f5d6c2017-10-06 12:06:10 -07003653 rcu_read_unlock();
David Ahern830218c2016-10-24 10:52:35 -07003654
3655 table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER;
3656}
3657
3658void rt6_purge_dflt_routers(struct net *net)
3659{
3660 struct fib6_table *table;
3661 struct hlist_head *head;
3662 unsigned int h;
3663
3664 rcu_read_lock();
3665
3666 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3667 head = &net->ipv6.fib_table_hash[h];
3668 hlist_for_each_entry_rcu(table, head, tb6_hlist) {
3669 if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER)
David Ahernafb1d4b52018-04-17 17:33:11 -07003670 __rt6_purge_dflt_routers(net, table);
David Ahern830218c2016-10-24 10:52:35 -07003671 }
3672 }
3673
3674 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675}
3676
Daniel Lezcano55786892008-03-04 13:47:47 -08003677static void rtmsg_to_fib6_config(struct net *net,
3678 struct in6_rtmsg *rtmsg,
Thomas Graf86872cb2006-08-22 00:01:08 -07003679 struct fib6_config *cfg)
3680{
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003681 *cfg = (struct fib6_config){
3682 .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ?
3683 : RT6_TABLE_MAIN,
3684 .fc_ifindex = rtmsg->rtmsg_ifindex,
David Ahern67f69512019-03-21 05:21:34 -07003685 .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER,
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003686 .fc_expires = rtmsg->rtmsg_info,
3687 .fc_dst_len = rtmsg->rtmsg_dst_len,
3688 .fc_src_len = rtmsg->rtmsg_src_len,
3689 .fc_flags = rtmsg->rtmsg_flags,
3690 .fc_type = rtmsg->rtmsg_type,
Thomas Graf86872cb2006-08-22 00:01:08 -07003691
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003692 .fc_nlinfo.nl_net = net,
Thomas Graf86872cb2006-08-22 00:01:08 -07003693
Maciej Żenczykowski8823a3a2018-09-29 23:44:52 -07003694 .fc_dst = rtmsg->rtmsg_dst,
3695 .fc_src = rtmsg->rtmsg_src,
3696 .fc_gateway = rtmsg->rtmsg_gateway,
3697 };
Thomas Graf86872cb2006-08-22 00:01:08 -07003698}
3699
Daniel Lezcano55786892008-03-04 13:47:47 -08003700int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701{
Thomas Graf86872cb2006-08-22 00:01:08 -07003702 struct fib6_config cfg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 struct in6_rtmsg rtmsg;
3704 int err;
3705
Ian Morris67ba4152014-08-24 21:53:10 +01003706 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707 case SIOCADDRT: /* Add a route */
3708 case SIOCDELRT: /* Delete a route */
Eric W. Biedermanaf31f412012-11-16 03:03:06 +00003709 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710 return -EPERM;
3711 err = copy_from_user(&rtmsg, arg,
3712 sizeof(struct in6_rtmsg));
3713 if (err)
3714 return -EFAULT;
Thomas Graf86872cb2006-08-22 00:01:08 -07003715
Daniel Lezcano55786892008-03-04 13:47:47 -08003716 rtmsg_to_fib6_config(net, &rtmsg, &cfg);
Thomas Graf86872cb2006-08-22 00:01:08 -07003717
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 rtnl_lock();
3719 switch (cmd) {
3720 case SIOCADDRT:
David Ahernacb54e32018-04-17 17:33:22 -07003721 err = ip6_route_add(&cfg, GFP_KERNEL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 break;
3723 case SIOCDELRT:
David Ahern333c4302017-05-21 10:12:04 -06003724 err = ip6_route_del(&cfg, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725 break;
3726 default:
3727 err = -EINVAL;
3728 }
3729 rtnl_unlock();
3730
3731 return err;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07003732 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733
3734 return -EINVAL;
3735}
3736
3737/*
3738 * Drop the packet on the floor
3739 */
3740
Brian Haleyd5fdd6b2009-06-23 04:31:07 -07003741static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003743 int type;
Eric Dumazetadf30902009-06-02 05:19:30 +00003744 struct dst_entry *dst = skb_dst(skb);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003745 switch (ipstats_mib_noroutes) {
3746 case IPSTATS_MIB_INNOROUTES:
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07003747 type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
Ulrich Weber45bb0062010-02-25 23:28:58 +00003748 if (type == IPV6_ADDR_ANY) {
Stephen Suryaputrabdb7cc62018-04-16 13:42:16 -04003749 IP6_INC_STATS(dev_net(dst->dev),
3750 __in6_dev_get_safely(skb->dev),
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003751 IPSTATS_MIB_INADDRERRORS);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003752 break;
3753 }
3754 /* FALLTHROUGH */
3755 case IPSTATS_MIB_OUTNOROUTES:
Denis V. Lunev3bd653c2008-10-08 10:54:51 -07003756 IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst),
3757 ipstats_mib_noroutes);
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003758 break;
3759 }
Alexey Dobriyan3ffe5332010-02-18 08:25:24 +00003760 icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761 kfree_skb(skb);
3762 return 0;
3763}
3764
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003765static int ip6_pkt_discard(struct sk_buff *skb)
3766{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003767 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003768}
3769
Eric W. Biedermanede20592015-10-07 16:48:47 -05003770static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771{
Eric Dumazetadf30902009-06-02 05:19:30 +00003772 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003773 return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003774}
3775
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003776static int ip6_pkt_prohibit(struct sk_buff *skb)
3777{
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003778 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003779}
3780
Eric W. Biedermanede20592015-10-07 16:48:47 -05003781static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb)
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003782{
Eric Dumazetadf30902009-06-02 05:19:30 +00003783 skb->dev = skb_dst(skb)->dev;
YOSHIFUJI Hideaki612f09e2007-04-13 16:18:02 -07003784 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
Thomas Graf9ce8ade2006-10-18 20:46:54 -07003785}
3786
Linus Torvalds1da177e2005-04-16 15:20:36 -07003787/*
3788 * Allocate a dst for local (unicast / anycast) address.
3789 */
3790
David Ahern360a9882018-04-18 15:39:00 -07003791struct fib6_info *addrconf_f6i_alloc(struct net *net,
3792 struct inet6_dev *idev,
3793 const struct in6_addr *addr,
3794 bool anycast, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795{
David Ahernc7a1ce32019-03-21 05:21:35 -07003796 struct fib6_config cfg = {
3797 .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL,
3798 .fc_ifindex = idev->dev->ifindex,
3799 .fc_flags = RTF_UP | RTF_ADDRCONF | RTF_NONEXTHOP,
3800 .fc_dst = *addr,
3801 .fc_dst_len = 128,
3802 .fc_protocol = RTPROT_KERNEL,
3803 .fc_nlinfo.nl_net = net,
3804 .fc_ignore_dev_down = true,
3805 };
David Ahern5f02ce242016-09-10 12:09:54 -07003806
David Aherne8478e82018-04-17 17:33:13 -07003807 if (anycast) {
David Ahernc7a1ce32019-03-21 05:21:35 -07003808 cfg.fc_type = RTN_ANYCAST;
3809 cfg.fc_flags |= RTF_ANYCAST;
David Aherne8478e82018-04-17 17:33:13 -07003810 } else {
David Ahernc7a1ce32019-03-21 05:21:35 -07003811 cfg.fc_type = RTN_LOCAL;
3812 cfg.fc_flags |= RTF_LOCAL;
David Aherne8478e82018-04-17 17:33:13 -07003813 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814
David Ahernc7a1ce32019-03-21 05:21:35 -07003815 return ip6_route_info_create(&cfg, gfp_flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003816}
3817
Daniel Walterc3968a82011-04-13 21:10:57 +00003818/* remove deleted ip from prefsrc entries */
3819struct arg_dev_net_ip {
3820 struct net_device *dev;
3821 struct net *net;
3822 struct in6_addr *addr;
3823};
3824
David Ahern8d1c8022018-04-17 17:33:26 -07003825static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg)
Daniel Walterc3968a82011-04-13 21:10:57 +00003826{
3827 struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev;
3828 struct net *net = ((struct arg_dev_net_ip *)arg)->net;
3829 struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
3830
David Ahernad1601a2019-03-27 20:53:56 -07003831 if (((void *)rt->fib6_nh.fib_nh_dev == dev || !dev) &&
David Ahern421842e2018-04-17 17:33:18 -07003832 rt != net->ipv6.fib6_null_entry &&
David Ahern93c2fb22018-04-18 15:38:59 -07003833 ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) {
Wei Wang60006a42017-10-06 12:05:58 -07003834 spin_lock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003835 /* remove prefsrc entry */
David Ahern93c2fb22018-04-18 15:38:59 -07003836 rt->fib6_prefsrc.plen = 0;
Wei Wang60006a42017-10-06 12:05:58 -07003837 spin_unlock_bh(&rt6_exception_lock);
Daniel Walterc3968a82011-04-13 21:10:57 +00003838 }
3839 return 0;
3840}
3841
3842void rt6_remove_prefsrc(struct inet6_ifaddr *ifp)
3843{
3844 struct net *net = dev_net(ifp->idev->dev);
3845 struct arg_dev_net_ip adni = {
3846 .dev = ifp->idev->dev,
3847 .net = net,
3848 .addr = &ifp->addr,
3849 };
Li RongQing0c3584d2013-12-27 16:32:38 +08003850 fib6_clean_all(net, fib6_remove_prefsrc, &adni);
Daniel Walterc3968a82011-04-13 21:10:57 +00003851}
3852
David Ahern2b2450c2019-03-27 20:53:52 -07003853#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003854
3855/* Remove routers and update dst entries when gateway turn into host. */
David Ahern8d1c8022018-04-17 17:33:26 -07003856static int fib6_clean_tohost(struct fib6_info *rt, void *arg)
Duan Jiongbe7a0102014-05-15 15:56:14 +08003857{
3858 struct in6_addr *gateway = (struct in6_addr *)arg;
3859
David Ahern93c2fb22018-04-18 15:38:59 -07003860 if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
David Ahernbdf00462019-04-05 16:30:26 -07003861 rt->fib6_nh.fib_nh_gw_family &&
David Ahernad1601a2019-03-27 20:53:56 -07003862 ipv6_addr_equal(gateway, &rt->fib6_nh.fib_nh_gw6)) {
Duan Jiongbe7a0102014-05-15 15:56:14 +08003863 return -1;
3864 }
Wei Wangb16cb452017-10-06 12:06:00 -07003865
3866 /* Further clean up cached routes in exception table.
3867 * This is needed because cached route may have a different
3868 * gateway than its 'parent' in the case of an ip redirect.
3869 */
3870 rt6_exceptions_clean_tohost(rt, gateway);
3871
Duan Jiongbe7a0102014-05-15 15:56:14 +08003872 return 0;
3873}
3874
3875void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
3876{
3877 fib6_clean_all(net, fib6_clean_tohost, gateway);
3878}
3879
Ido Schimmel2127d952018-01-07 12:45:03 +02003880struct arg_netdev_event {
3881 const struct net_device *dev;
Ido Schimmel4c981e22018-01-07 12:45:04 +02003882 union {
3883 unsigned int nh_flags;
3884 unsigned long event;
3885 };
Ido Schimmel2127d952018-01-07 12:45:03 +02003886};
3887
David Ahern8d1c8022018-04-17 17:33:26 -07003888static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003889{
David Ahern8d1c8022018-04-17 17:33:26 -07003890 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003891 struct fib6_node *fn;
3892
David Ahern93c2fb22018-04-18 15:38:59 -07003893 fn = rcu_dereference_protected(rt->fib6_node,
3894 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003895 iter = rcu_dereference_protected(fn->leaf,
David Ahern93c2fb22018-04-18 15:38:59 -07003896 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003897 while (iter) {
David Ahern93c2fb22018-04-18 15:38:59 -07003898 if (iter->fib6_metric == rt->fib6_metric &&
David Ahern33bd5ac2018-07-03 14:36:21 -07003899 rt6_qualify_for_ecmp(iter))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003900 return iter;
David Ahern8fb11a92018-05-04 13:54:24 -07003901 iter = rcu_dereference_protected(iter->fib6_next,
David Ahern93c2fb22018-04-18 15:38:59 -07003902 lockdep_is_held(&rt->fib6_table->tb6_lock));
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003903 }
3904
3905 return NULL;
3906}
3907
David Ahern8d1c8022018-04-17 17:33:26 -07003908static bool rt6_is_dead(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003909{
David Ahernad1601a2019-03-27 20:53:56 -07003910 if (rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD ||
3911 (rt->fib6_nh.fib_nh_flags & RTNH_F_LINKDOWN &&
3912 ip6_ignore_linkdown(rt->fib6_nh.fib_nh_dev)))
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003913 return true;
3914
3915 return false;
3916}
3917
David Ahern8d1c8022018-04-17 17:33:26 -07003918static int rt6_multipath_total_weight(const struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003919{
David Ahern8d1c8022018-04-17 17:33:26 -07003920 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003921 int total = 0;
3922
3923 if (!rt6_is_dead(rt))
David Ahernad1601a2019-03-27 20:53:56 -07003924 total += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003925
David Ahern93c2fb22018-04-18 15:38:59 -07003926 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003927 if (!rt6_is_dead(iter))
David Ahernad1601a2019-03-27 20:53:56 -07003928 total += iter->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003929 }
3930
3931 return total;
3932}
3933
David Ahern8d1c8022018-04-17 17:33:26 -07003934static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003935{
3936 int upper_bound = -1;
3937
3938 if (!rt6_is_dead(rt)) {
David Ahernad1601a2019-03-27 20:53:56 -07003939 *weight += rt->fib6_nh.fib_nh_weight;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003940 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
3941 total) - 1;
3942 }
David Ahernad1601a2019-03-27 20:53:56 -07003943 atomic_set(&rt->fib6_nh.fib_nh_upper_bound, upper_bound);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003944}
3945
David Ahern8d1c8022018-04-17 17:33:26 -07003946static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003947{
David Ahern8d1c8022018-04-17 17:33:26 -07003948 struct fib6_info *iter;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003949 int weight = 0;
3950
3951 rt6_upper_bound_set(rt, &weight, total);
3952
David Ahern93c2fb22018-04-18 15:38:59 -07003953 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003954 rt6_upper_bound_set(iter, &weight, total);
3955}
3956
David Ahern8d1c8022018-04-17 17:33:26 -07003957void rt6_multipath_rebalance(struct fib6_info *rt)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003958{
David Ahern8d1c8022018-04-17 17:33:26 -07003959 struct fib6_info *first;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003960 int total;
3961
3962 /* In case the entire multipath route was marked for flushing,
3963 * then there is no need to rebalance upon the removal of every
3964 * sibling route.
3965 */
David Ahern93c2fb22018-04-18 15:38:59 -07003966 if (!rt->fib6_nsiblings || rt->should_flush)
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003967 return;
3968
3969 /* During lookup routes are evaluated in order, so we need to
3970 * make sure upper bounds are assigned from the first sibling
3971 * onwards.
3972 */
3973 first = rt6_multipath_first_sibling(rt);
3974 if (WARN_ON_ONCE(!first))
3975 return;
3976
3977 total = rt6_multipath_total_weight(first);
3978 rt6_multipath_upper_bound_set(first, total);
3979}
3980
David Ahern8d1c8022018-04-17 17:33:26 -07003981static int fib6_ifup(struct fib6_info *rt, void *p_arg)
Ido Schimmel2127d952018-01-07 12:45:03 +02003982{
3983 const struct arg_netdev_event *arg = p_arg;
David Ahern7aef6852018-04-17 17:33:10 -07003984 struct net *net = dev_net(arg->dev);
Ido Schimmel2127d952018-01-07 12:45:03 +02003985
David Ahernad1601a2019-03-27 20:53:56 -07003986 if (rt != net->ipv6.fib6_null_entry &&
3987 rt->fib6_nh.fib_nh_dev == arg->dev) {
3988 rt->fib6_nh.fib_nh_flags &= ~arg->nh_flags;
David Ahern7aef6852018-04-17 17:33:10 -07003989 fib6_update_sernum_upto_root(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02003990 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02003991 }
Ido Schimmel2127d952018-01-07 12:45:03 +02003992
3993 return 0;
3994}
3995
3996void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
3997{
3998 struct arg_netdev_event arg = {
3999 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004000 {
4001 .nh_flags = nh_flags,
4002 },
Ido Schimmel2127d952018-01-07 12:45:03 +02004003 };
4004
4005 if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev))
4006 arg.nh_flags |= RTNH_F_LINKDOWN;
4007
4008 fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
4009}
4010
David Ahern8d1c8022018-04-17 17:33:26 -07004011static bool rt6_multipath_uses_dev(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004012 const struct net_device *dev)
4013{
David Ahern8d1c8022018-04-17 17:33:26 -07004014 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004015
David Ahernad1601a2019-03-27 20:53:56 -07004016 if (rt->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004017 return true;
David Ahern93c2fb22018-04-18 15:38:59 -07004018 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004019 if (iter->fib6_nh.fib_nh_dev == dev)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004020 return true;
4021
4022 return false;
4023}
4024
David Ahern8d1c8022018-04-17 17:33:26 -07004025static void rt6_multipath_flush(struct fib6_info *rt)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004026{
David Ahern8d1c8022018-04-17 17:33:26 -07004027 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004028
4029 rt->should_flush = 1;
David Ahern93c2fb22018-04-18 15:38:59 -07004030 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004031 iter->should_flush = 1;
4032}
4033
David Ahern8d1c8022018-04-17 17:33:26 -07004034static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004035 const struct net_device *down_dev)
4036{
David Ahern8d1c8022018-04-17 17:33:26 -07004037 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004038 unsigned int dead = 0;
4039
David Ahernad1601a2019-03-27 20:53:56 -07004040 if (rt->fib6_nh.fib_nh_dev == down_dev ||
4041 rt->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004042 dead++;
David Ahern93c2fb22018-04-18 15:38:59 -07004043 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004044 if (iter->fib6_nh.fib_nh_dev == down_dev ||
4045 iter->fib6_nh.fib_nh_flags & RTNH_F_DEAD)
Ido Schimmel1de178e2018-01-07 12:45:15 +02004046 dead++;
4047
4048 return dead;
4049}
4050
David Ahern8d1c8022018-04-17 17:33:26 -07004051static void rt6_multipath_nh_flags_set(struct fib6_info *rt,
Ido Schimmel1de178e2018-01-07 12:45:15 +02004052 const struct net_device *dev,
4053 unsigned int nh_flags)
4054{
David Ahern8d1c8022018-04-17 17:33:26 -07004055 struct fib6_info *iter;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004056
David Ahernad1601a2019-03-27 20:53:56 -07004057 if (rt->fib6_nh.fib_nh_dev == dev)
4058 rt->fib6_nh.fib_nh_flags |= nh_flags;
David Ahern93c2fb22018-04-18 15:38:59 -07004059 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings)
David Ahernad1601a2019-03-27 20:53:56 -07004060 if (iter->fib6_nh.fib_nh_dev == dev)
4061 iter->fib6_nh.fib_nh_flags |= nh_flags;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004062}
4063
David Aherna1a22c12017-01-18 07:40:36 -08004064/* called with write lock held for table with rt */
David Ahern8d1c8022018-04-17 17:33:26 -07004065static int fib6_ifdown(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004066{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004067 const struct arg_netdev_event *arg = p_arg;
4068 const struct net_device *dev = arg->dev;
David Ahern7aef6852018-04-17 17:33:10 -07004069 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004070
David Ahern421842e2018-04-17 17:33:18 -07004071 if (rt == net->ipv6.fib6_null_entry)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004072 return 0;
4073
4074 switch (arg->event) {
4075 case NETDEV_UNREGISTER:
David Ahernad1601a2019-03-27 20:53:56 -07004076 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004077 case NETDEV_DOWN:
Ido Schimmel1de178e2018-01-07 12:45:15 +02004078 if (rt->should_flush)
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004079 return -1;
David Ahern93c2fb22018-04-18 15:38:59 -07004080 if (!rt->fib6_nsiblings)
David Ahernad1601a2019-03-27 20:53:56 -07004081 return rt->fib6_nh.fib_nh_dev == dev ? -1 : 0;
Ido Schimmel1de178e2018-01-07 12:45:15 +02004082 if (rt6_multipath_uses_dev(rt, dev)) {
4083 unsigned int count;
4084
4085 count = rt6_multipath_dead_count(rt, dev);
David Ahern93c2fb22018-04-18 15:38:59 -07004086 if (rt->fib6_nsiblings + 1 == count) {
Ido Schimmel1de178e2018-01-07 12:45:15 +02004087 rt6_multipath_flush(rt);
4088 return -1;
4089 }
4090 rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD |
4091 RTNH_F_LINKDOWN);
David Ahern7aef6852018-04-17 17:33:10 -07004092 fib6_update_sernum(net, rt);
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004093 rt6_multipath_rebalance(rt);
Ido Schimmel1de178e2018-01-07 12:45:15 +02004094 }
4095 return -2;
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004096 case NETDEV_CHANGE:
David Ahernad1601a2019-03-27 20:53:56 -07004097 if (rt->fib6_nh.fib_nh_dev != dev ||
David Ahern93c2fb22018-04-18 15:38:59 -07004098 rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004099 break;
David Ahernad1601a2019-03-27 20:53:56 -07004100 rt->fib6_nh.fib_nh_flags |= RTNH_F_LINKDOWN;
Ido Schimmeld7dedee2018-01-09 16:40:25 +02004101 rt6_multipath_rebalance(rt);
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004102 break;
Ido Schimmel2b241362018-01-07 12:45:02 +02004103 }
David S. Millerc159d302011-12-26 15:24:36 -05004104
Linus Torvalds1da177e2005-04-16 15:20:36 -07004105 return 0;
4106}
4107
Ido Schimmel27c6fa72018-01-07 12:45:05 +02004108void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004109{
Ido Schimmel4c981e22018-01-07 12:45:04 +02004110 struct arg_netdev_event arg = {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004111 .dev = dev,
Ido Schimmel6802f3a2018-01-12 22:07:36 +02004112 {
4113 .event = event,
4114 },
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004115 };
David Ahern7c6bb7d2018-10-11 20:17:21 -07004116 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08004117
David Ahern7c6bb7d2018-10-11 20:17:21 -07004118 if (net->ipv6.sysctl.skip_notify_on_dev_down)
4119 fib6_clean_all_skip_notify(net, fib6_ifdown, &arg);
4120 else
4121 fib6_clean_all(net, fib6_ifdown, &arg);
Ido Schimmel4c981e22018-01-07 12:45:04 +02004122}
4123
4124void rt6_disable_ip(struct net_device *dev, unsigned long event)
4125{
4126 rt6_sync_down_dev(dev, event);
4127 rt6_uncached_list_flush_dev(dev_net(dev), dev);
4128 neigh_ifdown(&nd_tbl, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004129}
4130
Eric Dumazet95c96172012-04-15 05:58:06 +00004131struct rt6_mtu_change_arg {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004132 struct net_device *dev;
Eric Dumazet95c96172012-04-15 05:58:06 +00004133 unsigned int mtu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004134};
4135
David Ahern8d1c8022018-04-17 17:33:26 -07004136static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004137{
4138 struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
4139 struct inet6_dev *idev;
4140
4141 /* In IPv6 pmtu discovery is not optional,
4142 so that RTAX_MTU lock cannot disable it.
4143 We still use this lock to block changes
4144 caused by addrconf/ndisc.
4145 */
4146
4147 idev = __in6_dev_get(arg->dev);
David S. Miller38308472011-12-03 18:02:47 -05004148 if (!idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004149 return 0;
4150
4151 /* For administrative MTU increase, there is no way to discover
4152 IPv6 PMTU increase, so PMTU increase should be updated here.
4153 Since RFC 1981 doesn't include administrative MTU increase
4154 update PMTU increase is a MUST. (i.e. jumbo frame)
4155 */
David Ahernad1601a2019-03-27 20:53:56 -07004156 if (rt->fib6_nh.fib_nh_dev == arg->dev &&
David Ahernd4ead6b2018-04-17 17:33:16 -07004157 !fib6_metric_locked(rt, RTAX_MTU)) {
4158 u32 mtu = rt->fib6_pmtu;
4159
4160 if (mtu >= arg->mtu ||
4161 (mtu < arg->mtu && mtu == idev->cnf.mtu6))
4162 fib6_metric_set(rt, RTAX_MTU, arg->mtu);
4163
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004164 spin_lock_bh(&rt6_exception_lock);
Stefano Brivioe9fa1492018-03-06 11:10:19 +01004165 rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
Wei Wangf5bbe7e2017-10-06 12:05:59 -07004166 spin_unlock_bh(&rt6_exception_lock);
Simon Arlott566cfd82007-07-26 00:09:55 -07004167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004168 return 0;
4169}
4170
Eric Dumazet95c96172012-04-15 05:58:06 +00004171void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004172{
Thomas Grafc71099a2006-08-04 23:20:06 -07004173 struct rt6_mtu_change_arg arg = {
4174 .dev = dev,
4175 .mtu = mtu,
4176 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07004177
Li RongQing0c3584d2013-12-27 16:32:38 +08004178 fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004179}
4180
Patrick McHardyef7c79e2007-06-05 12:38:30 -07004181static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
Thomas Graf5176f912006-08-26 20:13:18 -07004182 [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004183 [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) },
Thomas Graf86872cb2006-08-22 00:01:08 -07004184 [RTA_OIF] = { .type = NLA_U32 },
Thomas Grafab364a62006-08-22 00:01:47 -07004185 [RTA_IIF] = { .type = NLA_U32 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004186 [RTA_PRIORITY] = { .type = NLA_U32 },
4187 [RTA_METRICS] = { .type = NLA_NESTED },
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004188 [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004189 [RTA_PREF] = { .type = NLA_U8 },
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004190 [RTA_ENCAP_TYPE] = { .type = NLA_U16 },
4191 [RTA_ENCAP] = { .type = NLA_NESTED },
Xin Long32bc2012015-12-16 17:50:11 +08004192 [RTA_EXPIRES] = { .type = NLA_U32 },
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004193 [RTA_UID] = { .type = NLA_U32 },
Liping Zhang3b45a412017-02-27 20:59:39 +08004194 [RTA_MARK] = { .type = NLA_U32 },
Eric Dumazetaa8f8772018-04-22 18:29:23 -07004195 [RTA_TABLE] = { .type = NLA_U32 },
Roopa Prabhueacb9382018-05-22 14:03:28 -07004196 [RTA_IP_PROTO] = { .type = NLA_U8 },
4197 [RTA_SPORT] = { .type = NLA_U16 },
4198 [RTA_DPORT] = { .type = NLA_U16 },
Thomas Graf86872cb2006-08-22 00:01:08 -07004199};
4200
4201static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
David Ahern333c4302017-05-21 10:12:04 -06004202 struct fib6_config *cfg,
4203 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004204{
Thomas Graf86872cb2006-08-22 00:01:08 -07004205 struct rtmsg *rtm;
4206 struct nlattr *tb[RTA_MAX+1];
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004207 unsigned int pref;
Thomas Graf86872cb2006-08-22 00:01:08 -07004208 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209
Johannes Bergfceb6432017-04-12 14:34:07 +02004210 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy,
David Aherndac9c972018-10-07 20:16:24 -07004211 extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004212 if (err < 0)
4213 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004214
Thomas Graf86872cb2006-08-22 00:01:08 -07004215 err = -EINVAL;
4216 rtm = nlmsg_data(nlh);
Thomas Graf86872cb2006-08-22 00:01:08 -07004217
Maciej Żenczykowski84db8402018-09-29 23:44:53 -07004218 *cfg = (struct fib6_config){
4219 .fc_table = rtm->rtm_table,
4220 .fc_dst_len = rtm->rtm_dst_len,
4221 .fc_src_len = rtm->rtm_src_len,
4222 .fc_flags = RTF_UP,
4223 .fc_protocol = rtm->rtm_protocol,
4224 .fc_type = rtm->rtm_type,
4225
4226 .fc_nlinfo.portid = NETLINK_CB(skb).portid,
4227 .fc_nlinfo.nlh = nlh,
4228 .fc_nlinfo.nl_net = sock_net(skb->sk),
4229 };
Thomas Graf86872cb2006-08-22 00:01:08 -07004230
Nicolas Dichtelef2c7d72012-09-05 02:12:42 +00004231 if (rtm->rtm_type == RTN_UNREACHABLE ||
4232 rtm->rtm_type == RTN_BLACKHOLE ||
Nicolas Dichtelb4949ab2012-09-06 05:53:35 +00004233 rtm->rtm_type == RTN_PROHIBIT ||
4234 rtm->rtm_type == RTN_THROW)
Thomas Graf86872cb2006-08-22 00:01:08 -07004235 cfg->fc_flags |= RTF_REJECT;
4236
Maciej Żenczykowskiab79ad12010-09-27 00:07:02 +00004237 if (rtm->rtm_type == RTN_LOCAL)
4238 cfg->fc_flags |= RTF_LOCAL;
4239
Martin KaFai Lau1f56a01f2015-04-28 13:03:03 -07004240 if (rtm->rtm_flags & RTM_F_CLONED)
4241 cfg->fc_flags |= RTF_CACHE;
4242
David Ahernfc1e64e2018-01-25 16:55:09 -08004243 cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK);
4244
Thomas Graf86872cb2006-08-22 00:01:08 -07004245 if (tb[RTA_GATEWAY]) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004246 cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]);
Thomas Graf86872cb2006-08-22 00:01:08 -07004247 cfg->fc_flags |= RTF_GATEWAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004248 }
David Aherne3818542019-02-26 09:00:03 -08004249 if (tb[RTA_VIA]) {
4250 NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute");
4251 goto errout;
4252 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004253
4254 if (tb[RTA_DST]) {
4255 int plen = (rtm->rtm_dst_len + 7) >> 3;
4256
4257 if (nla_len(tb[RTA_DST]) < plen)
4258 goto errout;
4259
4260 nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004261 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004262
4263 if (tb[RTA_SRC]) {
4264 int plen = (rtm->rtm_src_len + 7) >> 3;
4265
4266 if (nla_len(tb[RTA_SRC]) < plen)
4267 goto errout;
4268
4269 nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004270 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004271
Daniel Walterc3968a82011-04-13 21:10:57 +00004272 if (tb[RTA_PREFSRC])
Jiri Benc67b61f62015-03-29 16:59:26 +02004273 cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]);
Daniel Walterc3968a82011-04-13 21:10:57 +00004274
Thomas Graf86872cb2006-08-22 00:01:08 -07004275 if (tb[RTA_OIF])
4276 cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]);
4277
4278 if (tb[RTA_PRIORITY])
4279 cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]);
4280
4281 if (tb[RTA_METRICS]) {
4282 cfg->fc_mx = nla_data(tb[RTA_METRICS]);
4283 cfg->fc_mx_len = nla_len(tb[RTA_METRICS]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004284 }
Thomas Graf86872cb2006-08-22 00:01:08 -07004285
4286 if (tb[RTA_TABLE])
4287 cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
4288
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004289 if (tb[RTA_MULTIPATH]) {
4290 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
4291 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
David Ahern9ed59592017-01-17 14:57:36 -08004292
4293 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
David Ahernc255bd62017-05-27 16:19:27 -06004294 cfg->fc_mp_len, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004295 if (err < 0)
4296 goto errout;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004297 }
4298
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004299 if (tb[RTA_PREF]) {
4300 pref = nla_get_u8(tb[RTA_PREF]);
4301 if (pref != ICMPV6_ROUTER_PREF_LOW &&
4302 pref != ICMPV6_ROUTER_PREF_HIGH)
4303 pref = ICMPV6_ROUTER_PREF_MEDIUM;
4304 cfg->fc_flags |= RTF_PREF(pref);
4305 }
4306
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004307 if (tb[RTA_ENCAP])
4308 cfg->fc_encap = tb[RTA_ENCAP];
4309
David Ahern9ed59592017-01-17 14:57:36 -08004310 if (tb[RTA_ENCAP_TYPE]) {
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004311 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
4312
David Ahernc255bd62017-05-27 16:19:27 -06004313 err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack);
David Ahern9ed59592017-01-17 14:57:36 -08004314 if (err < 0)
4315 goto errout;
4316 }
4317
Xin Long32bc2012015-12-16 17:50:11 +08004318 if (tb[RTA_EXPIRES]) {
4319 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
4320
4321 if (addrconf_finite_timeout(timeout)) {
4322 cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
4323 cfg->fc_flags |= RTF_EXPIRES;
4324 }
4325 }
4326
Thomas Graf86872cb2006-08-22 00:01:08 -07004327 err = 0;
4328errout:
4329 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004330}
4331
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004332struct rt6_nh {
David Ahern8d1c8022018-04-17 17:33:26 -07004333 struct fib6_info *fib6_info;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004334 struct fib6_config r_cfg;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004335 struct list_head next;
4336};
4337
David Ahernd4ead6b2018-04-17 17:33:16 -07004338static int ip6_route_info_append(struct net *net,
4339 struct list_head *rt6_nh_list,
David Ahern8d1c8022018-04-17 17:33:26 -07004340 struct fib6_info *rt,
4341 struct fib6_config *r_cfg)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004342{
4343 struct rt6_nh *nh;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004344 int err = -EEXIST;
4345
4346 list_for_each_entry(nh, rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004347 /* check if fib6_info already exists */
4348 if (rt6_duplicate_nexthop(nh->fib6_info, rt))
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004349 return err;
4350 }
4351
4352 nh = kzalloc(sizeof(*nh), GFP_KERNEL);
4353 if (!nh)
4354 return -ENOMEM;
David Ahern8d1c8022018-04-17 17:33:26 -07004355 nh->fib6_info = rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004356 memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg));
4357 list_add_tail(&nh->next, rt6_nh_list);
4358
4359 return 0;
4360}
4361
David Ahern8d1c8022018-04-17 17:33:26 -07004362static void ip6_route_mpath_notify(struct fib6_info *rt,
4363 struct fib6_info *rt_last,
David Ahern3b1137f2017-02-02 12:37:10 -08004364 struct nl_info *info,
4365 __u16 nlflags)
4366{
4367 /* if this is an APPEND route, then rt points to the first route
4368 * inserted and rt_last points to last route inserted. Userspace
4369 * wants a consistent dump of the route which starts at the first
4370 * nexthop. Since sibling routes are always added at the end of
4371 * the list, find the first sibling of the last route appended
4372 */
David Ahern93c2fb22018-04-18 15:38:59 -07004373 if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
4374 rt = list_first_entry(&rt_last->fib6_siblings,
David Ahern8d1c8022018-04-17 17:33:26 -07004375 struct fib6_info,
David Ahern93c2fb22018-04-18 15:38:59 -07004376 fib6_siblings);
David Ahern3b1137f2017-02-02 12:37:10 -08004377 }
4378
4379 if (rt)
4380 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4381}
4382
David Ahern333c4302017-05-21 10:12:04 -06004383static int ip6_route_multipath_add(struct fib6_config *cfg,
4384 struct netlink_ext_ack *extack)
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004385{
David Ahern8d1c8022018-04-17 17:33:26 -07004386 struct fib6_info *rt_notif = NULL, *rt_last = NULL;
David Ahern3b1137f2017-02-02 12:37:10 -08004387 struct nl_info *info = &cfg->fc_nlinfo;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004388 struct fib6_config r_cfg;
4389 struct rtnexthop *rtnh;
David Ahern8d1c8022018-04-17 17:33:26 -07004390 struct fib6_info *rt;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004391 struct rt6_nh *err_nh;
4392 struct rt6_nh *nh, *nh_safe;
David Ahern3b1137f2017-02-02 12:37:10 -08004393 __u16 nlflags;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004394 int remaining;
4395 int attrlen;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004396 int err = 1;
4397 int nhn = 0;
4398 int replace = (cfg->fc_nlinfo.nlh &&
4399 (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
4400 LIST_HEAD(rt6_nh_list);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004401
David Ahern3b1137f2017-02-02 12:37:10 -08004402 nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
4403 if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
4404 nlflags |= NLM_F_APPEND;
4405
Michal Kubeček35f1b4e2015-05-18 20:53:55 +02004406 remaining = cfg->fc_mp_len;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004407 rtnh = (struct rtnexthop *)cfg->fc_mp;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004408
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004409 /* Parse a Multipath Entry and build a list (rt6_nh_list) of
David Ahern8d1c8022018-04-17 17:33:26 -07004410 * fib6_info structs per nexthop
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004411 */
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004412 while (rtnh_ok(rtnh, remaining)) {
4413 memcpy(&r_cfg, cfg, sizeof(*cfg));
4414 if (rtnh->rtnh_ifindex)
4415 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4416
4417 attrlen = rtnh_attrlen(rtnh);
4418 if (attrlen > 0) {
4419 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4420
4421 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4422 if (nla) {
Jiri Benc67b61f62015-03-29 16:59:26 +02004423 r_cfg.fc_gateway = nla_get_in6_addr(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004424 r_cfg.fc_flags |= RTF_GATEWAY;
4425 }
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004426 r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP);
4427 nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
4428 if (nla)
4429 r_cfg.fc_encap_type = nla_get_u16(nla);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004430 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004431
David Ahern68e2ffd2018-03-20 10:06:59 -07004432 r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK);
David Ahernacb54e32018-04-17 17:33:22 -07004433 rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack);
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004434 if (IS_ERR(rt)) {
4435 err = PTR_ERR(rt);
4436 rt = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004437 goto cleanup;
Roopa Prabhu8c5b83f2015-10-10 08:26:36 -07004438 }
David Ahernb5d2d752018-07-15 09:35:19 -07004439 if (!rt6_qualify_for_ecmp(rt)) {
4440 err = -EINVAL;
4441 NL_SET_ERR_MSG(extack,
4442 "Device only routes can not be added for IPv6 using the multipath API.");
4443 fib6_info_release(rt);
4444 goto cleanup;
4445 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004446
David Ahernad1601a2019-03-27 20:53:56 -07004447 rt->fib6_nh.fib_nh_weight = rtnh->rtnh_hops + 1;
Ido Schimmel398958a2018-01-09 16:40:28 +02004448
David Ahernd4ead6b2018-04-17 17:33:16 -07004449 err = ip6_route_info_append(info->nl_net, &rt6_nh_list,
4450 rt, &r_cfg);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004451 if (err) {
David Ahern93531c62018-04-17 17:33:25 -07004452 fib6_info_release(rt);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004453 goto cleanup;
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004454 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004455
4456 rtnh = rtnh_next(rtnh, &remaining);
4457 }
4458
David Ahern3b1137f2017-02-02 12:37:10 -08004459 /* for add and replace send one notification with all nexthops.
4460 * Skip the notification in fib6_add_rt2node and send one with
4461 * the full route when done
4462 */
4463 info->skip_notify = 1;
4464
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004465 err_nh = NULL;
4466 list_for_each_entry(nh, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004467 err = __ip6_ins_rt(nh->fib6_info, info, extack);
4468 fib6_info_release(nh->fib6_info);
David Ahern3b1137f2017-02-02 12:37:10 -08004469
David Ahernf7225172018-06-04 13:41:42 -07004470 if (!err) {
4471 /* save reference to last route successfully inserted */
4472 rt_last = nh->fib6_info;
4473
4474 /* save reference to first route for notification */
4475 if (!rt_notif)
4476 rt_notif = nh->fib6_info;
4477 }
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004478
David Ahern8d1c8022018-04-17 17:33:26 -07004479 /* nh->fib6_info is used or freed at this point, reset to NULL*/
4480 nh->fib6_info = NULL;
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004481 if (err) {
4482 if (replace && nhn)
Jakub Kicinskia5a82d82019-01-14 10:52:45 -08004483 NL_SET_ERR_MSG_MOD(extack,
4484 "multipath route replace failed (check consistency of installed routes)");
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004485 err_nh = nh;
4486 goto add_errout;
4487 }
4488
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004489 /* Because each route is added like a single route we remove
Michal Kubeček27596472015-05-18 20:54:00 +02004490 * these flags after the first nexthop: if there is a collision,
4491 * we have already failed to add the first nexthop:
4492 * fib6_add_rt2node() has rejected it; when replacing, old
4493 * nexthops have been replaced by first new, the rest should
4494 * be added to it.
Nicolas Dichtel1a724182012-11-01 22:58:22 +00004495 */
Michal Kubeček27596472015-05-18 20:54:00 +02004496 cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
4497 NLM_F_REPLACE);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004498 nhn++;
4499 }
4500
David Ahern3b1137f2017-02-02 12:37:10 -08004501 /* success ... tell user about new route */
4502 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004503 goto cleanup;
4504
4505add_errout:
David Ahern3b1137f2017-02-02 12:37:10 -08004506 /* send notification for routes that were added so that
4507 * the delete notifications sent by ip6_route_del are
4508 * coherent
4509 */
4510 if (rt_notif)
4511 ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
4512
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004513 /* Delete routes that were already added */
4514 list_for_each_entry(nh, &rt6_nh_list, next) {
4515 if (err_nh == nh)
4516 break;
David Ahern333c4302017-05-21 10:12:04 -06004517 ip6_route_del(&nh->r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004518 }
4519
4520cleanup:
4521 list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) {
David Ahern8d1c8022018-04-17 17:33:26 -07004522 if (nh->fib6_info)
4523 fib6_info_release(nh->fib6_info);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004524 list_del(&nh->next);
4525 kfree(nh);
4526 }
4527
4528 return err;
4529}
4530
David Ahern333c4302017-05-21 10:12:04 -06004531static int ip6_route_multipath_del(struct fib6_config *cfg,
4532 struct netlink_ext_ack *extack)
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004533{
4534 struct fib6_config r_cfg;
4535 struct rtnexthop *rtnh;
4536 int remaining;
4537 int attrlen;
4538 int err = 1, last_err = 0;
4539
4540 remaining = cfg->fc_mp_len;
4541 rtnh = (struct rtnexthop *)cfg->fc_mp;
4542
4543 /* Parse a Multipath Entry */
4544 while (rtnh_ok(rtnh, remaining)) {
4545 memcpy(&r_cfg, cfg, sizeof(*cfg));
4546 if (rtnh->rtnh_ifindex)
4547 r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
4548
4549 attrlen = rtnh_attrlen(rtnh);
4550 if (attrlen > 0) {
4551 struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
4552
4553 nla = nla_find(attrs, attrlen, RTA_GATEWAY);
4554 if (nla) {
4555 nla_memcpy(&r_cfg.fc_gateway, nla, 16);
4556 r_cfg.fc_flags |= RTF_GATEWAY;
4557 }
4558 }
David Ahern333c4302017-05-21 10:12:04 -06004559 err = ip6_route_del(&r_cfg, extack);
Roopa Prabhu6b9ea5a2015-09-08 10:53:04 -07004560 if (err)
4561 last_err = err;
4562
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004563 rtnh = rtnh_next(rtnh, &remaining);
4564 }
4565
4566 return last_err;
4567}
4568
David Ahernc21ef3e2017-04-16 09:48:24 -07004569static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4570 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004571{
Thomas Graf86872cb2006-08-22 00:01:08 -07004572 struct fib6_config cfg;
4573 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004574
David Ahern333c4302017-05-21 10:12:04 -06004575 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004576 if (err < 0)
4577 return err;
4578
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004579 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004580 return ip6_route_multipath_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004581 else {
4582 cfg.fc_delete_all_nh = 1;
David Ahern333c4302017-05-21 10:12:04 -06004583 return ip6_route_del(&cfg, extack);
David Ahern0ae81332017-02-02 12:37:08 -08004584 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004585}
4586
David Ahernc21ef3e2017-04-16 09:48:24 -07004587static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
4588 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004589{
Thomas Graf86872cb2006-08-22 00:01:08 -07004590 struct fib6_config cfg;
4591 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004592
David Ahern333c4302017-05-21 10:12:04 -06004593 err = rtm_to_fib6_config(skb, nlh, &cfg, extack);
Thomas Graf86872cb2006-08-22 00:01:08 -07004594 if (err < 0)
4595 return err;
4596
David Ahern67f69512019-03-21 05:21:34 -07004597 if (cfg.fc_metric == 0)
4598 cfg.fc_metric = IP6_RT_PRIO_USER;
4599
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004600 if (cfg.fc_mp)
David Ahern333c4302017-05-21 10:12:04 -06004601 return ip6_route_multipath_add(&cfg, extack);
Nicolas Dichtel51ebd312012-10-22 03:42:09 +00004602 else
David Ahernacb54e32018-04-17 17:33:22 -07004603 return ip6_route_add(&cfg, GFP_KERNEL, extack);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004604}
4605
David Ahern8d1c8022018-04-17 17:33:26 -07004606static size_t rt6_nlmsg_size(struct fib6_info *rt)
Thomas Graf339bf982006-11-10 14:10:15 -08004607{
David Ahernbeb1afac52017-02-02 12:37:09 -08004608 int nexthop_len = 0;
4609
David Ahern93c2fb22018-04-18 15:38:59 -07004610 if (rt->fib6_nsiblings) {
David Ahernbeb1afac52017-02-02 12:37:09 -08004611 nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
4612 + NLA_ALIGN(sizeof(struct rtnexthop))
4613 + nla_total_size(16) /* RTA_GATEWAY */
David Ahernad1601a2019-03-27 20:53:56 -07004614 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws);
David Ahernbeb1afac52017-02-02 12:37:09 -08004615
David Ahern93c2fb22018-04-18 15:38:59 -07004616 nexthop_len *= rt->fib6_nsiblings;
David Ahernbeb1afac52017-02-02 12:37:09 -08004617 }
4618
Thomas Graf339bf982006-11-10 14:10:15 -08004619 return NLMSG_ALIGN(sizeof(struct rtmsg))
4620 + nla_total_size(16) /* RTA_SRC */
4621 + nla_total_size(16) /* RTA_DST */
4622 + nla_total_size(16) /* RTA_GATEWAY */
4623 + nla_total_size(16) /* RTA_PREFSRC */
4624 + nla_total_size(4) /* RTA_TABLE */
4625 + nla_total_size(4) /* RTA_IIF */
4626 + nla_total_size(4) /* RTA_OIF */
4627 + nla_total_size(4) /* RTA_PRIORITY */
Noriaki TAKAMIYA6a2b9ce2007-01-23 22:09:41 -08004628 + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
Daniel Borkmannea697632015-01-05 23:57:47 +01004629 + nla_total_size(sizeof(struct rta_cacheinfo))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004630 + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004631 + nla_total_size(1) /* RTA_PREF */
David Ahernad1601a2019-03-27 20:53:56 -07004632 + lwtunnel_get_encap_size(rt->fib6_nh.fib_nh_lws)
David Ahernbeb1afac52017-02-02 12:37:09 -08004633 + nexthop_len;
4634}
4635
David Ahernd4ead6b2018-04-17 17:33:16 -07004636static int rt6_fill_node(struct net *net, struct sk_buff *skb,
David Ahern8d1c8022018-04-17 17:33:26 -07004637 struct fib6_info *rt, struct dst_entry *dst,
David Ahernd4ead6b2018-04-17 17:33:16 -07004638 struct in6_addr *dest, struct in6_addr *src,
Eric W. Biederman15e47302012-09-07 20:12:54 +00004639 int iif, int type, u32 portid, u32 seq,
David Ahernf8cfe2c2017-01-17 15:51:08 -08004640 unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004641{
Xin Long22d0bd82018-09-11 14:33:58 +08004642 struct rt6_info *rt6 = (struct rt6_info *)dst;
4643 struct rt6key *rt6_dst, *rt6_src;
4644 u32 *pmetrics, table, rt6_flags;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004645 struct nlmsghdr *nlh;
Xin Long22d0bd82018-09-11 14:33:58 +08004646 struct rtmsg *rtm;
David Ahernd4ead6b2018-04-17 17:33:16 -07004647 long expires = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004648
Eric W. Biederman15e47302012-09-07 20:12:54 +00004649 nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags);
David S. Miller38308472011-12-03 18:02:47 -05004650 if (!nlh)
Patrick McHardy26932562007-01-31 23:16:40 -08004651 return -EMSGSIZE;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004652
Xin Long22d0bd82018-09-11 14:33:58 +08004653 if (rt6) {
4654 rt6_dst = &rt6->rt6i_dst;
4655 rt6_src = &rt6->rt6i_src;
4656 rt6_flags = rt6->rt6i_flags;
4657 } else {
4658 rt6_dst = &rt->fib6_dst;
4659 rt6_src = &rt->fib6_src;
4660 rt6_flags = rt->fib6_flags;
4661 }
4662
Thomas Graf2d7202b2006-08-22 00:01:27 -07004663 rtm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004664 rtm->rtm_family = AF_INET6;
Xin Long22d0bd82018-09-11 14:33:58 +08004665 rtm->rtm_dst_len = rt6_dst->plen;
4666 rtm->rtm_src_len = rt6_src->plen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004667 rtm->rtm_tos = 0;
David Ahern93c2fb22018-04-18 15:38:59 -07004668 if (rt->fib6_table)
4669 table = rt->fib6_table->tb6_id;
Thomas Grafc71099a2006-08-04 23:20:06 -07004670 else
Patrick McHardy9e762a42006-08-10 23:09:48 -07004671 table = RT6_TABLE_UNSPEC;
Kalash Nainwal97f00822019-02-20 16:23:04 -08004672 rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT;
David S. Millerc78679e2012-04-01 20:27:33 -04004673 if (nla_put_u32(skb, RTA_TABLE, table))
4674 goto nla_put_failure;
David Aherne8478e82018-04-17 17:33:13 -07004675
4676 rtm->rtm_type = rt->fib6_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677 rtm->rtm_flags = 0;
4678 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
David Ahern93c2fb22018-04-18 15:38:59 -07004679 rtm->rtm_protocol = rt->fib6_protocol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680
Xin Long22d0bd82018-09-11 14:33:58 +08004681 if (rt6_flags & RTF_CACHE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004682 rtm->rtm_flags |= RTM_F_CLONED;
4683
David Ahernd4ead6b2018-04-17 17:33:16 -07004684 if (dest) {
4685 if (nla_put_in6_addr(skb, RTA_DST, dest))
David S. Millerc78679e2012-04-01 20:27:33 -04004686 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004687 rtm->rtm_dst_len = 128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688 } else if (rtm->rtm_dst_len)
Xin Long22d0bd82018-09-11 14:33:58 +08004689 if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004690 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691#ifdef CONFIG_IPV6_SUBTREES
4692 if (src) {
Jiri Benc930345e2015-03-29 16:59:25 +02004693 if (nla_put_in6_addr(skb, RTA_SRC, src))
David S. Millerc78679e2012-04-01 20:27:33 -04004694 goto nla_put_failure;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09004695 rtm->rtm_src_len = 128;
David S. Millerc78679e2012-04-01 20:27:33 -04004696 } else if (rtm->rtm_src_len &&
Xin Long22d0bd82018-09-11 14:33:58 +08004697 nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr))
David S. Millerc78679e2012-04-01 20:27:33 -04004698 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004699#endif
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004700 if (iif) {
4701#ifdef CONFIG_IPV6_MROUTE
Xin Long22d0bd82018-09-11 14:33:58 +08004702 if (ipv6_addr_is_multicast(&rt6_dst->addr)) {
David Ahernfd61c6b2017-01-17 15:51:07 -08004703 int err = ip6mr_get_route(net, skb, rtm, portid);
Nikolay Aleksandrov2cf75072016-09-25 23:08:31 +02004704
David Ahernfd61c6b2017-01-17 15:51:07 -08004705 if (err == 0)
4706 return 0;
4707 if (err < 0)
4708 goto nla_put_failure;
YOSHIFUJI Hideaki7bc570c2008-04-03 09:22:53 +09004709 } else
4710#endif
David S. Millerc78679e2012-04-01 20:27:33 -04004711 if (nla_put_u32(skb, RTA_IIF, iif))
4712 goto nla_put_failure;
David Ahernd4ead6b2018-04-17 17:33:16 -07004713 } else if (dest) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004714 struct in6_addr saddr_buf;
David Ahernd4ead6b2018-04-17 17:33:16 -07004715 if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
Jiri Benc930345e2015-03-29 16:59:25 +02004716 nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004717 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004718 }
Thomas Graf2d7202b2006-08-22 00:01:27 -07004719
David Ahern93c2fb22018-04-18 15:38:59 -07004720 if (rt->fib6_prefsrc.plen) {
Daniel Walterc3968a82011-04-13 21:10:57 +00004721 struct in6_addr saddr_buf;
David Ahern93c2fb22018-04-18 15:38:59 -07004722 saddr_buf = rt->fib6_prefsrc.addr;
Jiri Benc930345e2015-03-29 16:59:25 +02004723 if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
David S. Millerc78679e2012-04-01 20:27:33 -04004724 goto nla_put_failure;
Daniel Walterc3968a82011-04-13 21:10:57 +00004725 }
4726
David Ahernd4ead6b2018-04-17 17:33:16 -07004727 pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics;
4728 if (rtnetlink_put_metrics(skb, pmetrics) < 0)
Thomas Graf2d7202b2006-08-22 00:01:27 -07004729 goto nla_put_failure;
4730
David Ahern93c2fb22018-04-18 15:38:59 -07004731 if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric))
David S. Millerc78679e2012-04-01 20:27:33 -04004732 goto nla_put_failure;
Li Wei82539472012-07-29 16:01:30 +00004733
David Ahernbeb1afac52017-02-02 12:37:09 -08004734 /* For multipath routes, walk the siblings list and add
4735 * each as a nexthop within RTA_MULTIPATH.
4736 */
Xin Long22d0bd82018-09-11 14:33:58 +08004737 if (rt6) {
4738 if (rt6_flags & RTF_GATEWAY &&
4739 nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway))
4740 goto nla_put_failure;
4741
4742 if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex))
4743 goto nla_put_failure;
4744 } else if (rt->fib6_nsiblings) {
David Ahern8d1c8022018-04-17 17:33:26 -07004745 struct fib6_info *sibling, *next_sibling;
David Ahernbeb1afac52017-02-02 12:37:09 -08004746 struct nlattr *mp;
4747
4748 mp = nla_nest_start(skb, RTA_MULTIPATH);
4749 if (!mp)
4750 goto nla_put_failure;
4751
David Ahernc0a72072019-04-02 14:11:58 -07004752 if (fib_add_nexthop(skb, &rt->fib6_nh.nh_common,
4753 rt->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004754 goto nla_put_failure;
4755
4756 list_for_each_entry_safe(sibling, next_sibling,
David Ahern93c2fb22018-04-18 15:38:59 -07004757 &rt->fib6_siblings, fib6_siblings) {
David Ahernc0a72072019-04-02 14:11:58 -07004758 if (fib_add_nexthop(skb, &sibling->fib6_nh.nh_common,
4759 sibling->fib6_nh.fib_nh_weight) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004760 goto nla_put_failure;
4761 }
4762
4763 nla_nest_end(skb, mp);
4764 } else {
David Ahernc0a72072019-04-02 14:11:58 -07004765 if (fib_nexthop_info(skb, &rt->fib6_nh.nh_common,
4766 &rtm->rtm_flags, false) < 0)
David Ahernbeb1afac52017-02-02 12:37:09 -08004767 goto nla_put_failure;
4768 }
4769
Xin Long22d0bd82018-09-11 14:33:58 +08004770 if (rt6_flags & RTF_EXPIRES) {
David Ahern14895682018-04-17 17:33:17 -07004771 expires = dst ? dst->expires : rt->expires;
4772 expires -= jiffies;
4773 }
YOSHIFUJI Hideaki69cdf8f2008-05-19 16:55:13 -07004774
David Ahernd4ead6b2018-04-17 17:33:16 -07004775 if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
Thomas Grafe3703b32006-11-27 09:27:07 -08004776 goto nla_put_failure;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777
Xin Long22d0bd82018-09-11 14:33:58 +08004778 if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
Lubomir Rintelc78ba6d2015-03-11 15:39:21 +01004779 goto nla_put_failure;
4780
Roopa Prabhu19e42e42015-07-21 10:43:48 +02004781
Johannes Berg053c0952015-01-16 22:09:00 +01004782 nlmsg_end(skb, nlh);
4783 return 0;
Thomas Graf2d7202b2006-08-22 00:01:27 -07004784
4785nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08004786 nlmsg_cancel(skb, nlh);
4787 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004788}
4789
David Ahern13e38902018-10-15 18:56:44 -07004790static bool fib6_info_uses_dev(const struct fib6_info *f6i,
4791 const struct net_device *dev)
4792{
David Ahernad1601a2019-03-27 20:53:56 -07004793 if (f6i->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004794 return true;
4795
4796 if (f6i->fib6_nsiblings) {
4797 struct fib6_info *sibling, *next_sibling;
4798
4799 list_for_each_entry_safe(sibling, next_sibling,
4800 &f6i->fib6_siblings, fib6_siblings) {
David Ahernad1601a2019-03-27 20:53:56 -07004801 if (sibling->fib6_nh.fib_nh_dev == dev)
David Ahern13e38902018-10-15 18:56:44 -07004802 return true;
4803 }
4804 }
4805
4806 return false;
4807}
4808
David Ahern8d1c8022018-04-17 17:33:26 -07004809int rt6_dump_route(struct fib6_info *rt, void *p_arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004810{
4811 struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
David Ahern13e38902018-10-15 18:56:44 -07004812 struct fib_dump_filter *filter = &arg->filter;
4813 unsigned int flags = NLM_F_MULTI;
David Ahern1f17e2f2017-01-26 13:54:08 -08004814 struct net *net = arg->net;
4815
David Ahern421842e2018-04-17 17:33:18 -07004816 if (rt == net->ipv6.fib6_null_entry)
David Ahern1f17e2f2017-01-26 13:54:08 -08004817 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004818
David Ahern13e38902018-10-15 18:56:44 -07004819 if ((filter->flags & RTM_F_PREFIX) &&
4820 !(rt->fib6_flags & RTF_PREFIX_RT)) {
4821 /* success since this is not a prefix route */
4822 return 1;
4823 }
4824 if (filter->filter_set) {
4825 if ((filter->rt_type && rt->fib6_type != filter->rt_type) ||
4826 (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) ||
4827 (filter->protocol && rt->fib6_protocol != filter->protocol)) {
David Ahernf8cfe2c2017-01-17 15:51:08 -08004828 return 1;
4829 }
David Ahern13e38902018-10-15 18:56:44 -07004830 flags |= NLM_F_DUMP_FILTERED;
David Ahernf8cfe2c2017-01-17 15:51:08 -08004831 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004832
David Ahernd4ead6b2018-04-17 17:33:16 -07004833 return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0,
4834 RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid,
David Ahern13e38902018-10-15 18:56:44 -07004835 arg->cb->nlh->nlmsg_seq, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004836}
4837
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004838static int inet6_rtm_valid_getroute_req(struct sk_buff *skb,
4839 const struct nlmsghdr *nlh,
4840 struct nlattr **tb,
4841 struct netlink_ext_ack *extack)
4842{
4843 struct rtmsg *rtm;
4844 int i, err;
4845
4846 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
4847 NL_SET_ERR_MSG_MOD(extack,
4848 "Invalid header for get route request");
4849 return -EINVAL;
4850 }
4851
4852 if (!netlink_strict_get_check(skb))
4853 return nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX,
4854 rtm_ipv6_policy, extack);
4855
4856 rtm = nlmsg_data(nlh);
4857 if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) ||
4858 (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) ||
4859 rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope ||
4860 rtm->rtm_type) {
4861 NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
4862 return -EINVAL;
4863 }
4864 if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
4865 NL_SET_ERR_MSG_MOD(extack,
4866 "Invalid flags for get route request");
4867 return -EINVAL;
4868 }
4869
4870 err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
4871 rtm_ipv6_policy, extack);
4872 if (err)
4873 return err;
4874
4875 if ((tb[RTA_SRC] && !rtm->rtm_src_len) ||
4876 (tb[RTA_DST] && !rtm->rtm_dst_len)) {
4877 NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6");
4878 return -EINVAL;
4879 }
4880
4881 for (i = 0; i <= RTA_MAX; i++) {
4882 if (!tb[i])
4883 continue;
4884
4885 switch (i) {
4886 case RTA_SRC:
4887 case RTA_DST:
4888 case RTA_IIF:
4889 case RTA_OIF:
4890 case RTA_MARK:
4891 case RTA_UID:
4892 case RTA_SPORT:
4893 case RTA_DPORT:
4894 case RTA_IP_PROTO:
4895 break;
4896 default:
4897 NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
4898 return -EINVAL;
4899 }
4900 }
4901
4902 return 0;
4903}
4904
David Ahernc21ef3e2017-04-16 09:48:24 -07004905static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
4906 struct netlink_ext_ack *extack)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004907{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09004908 struct net *net = sock_net(in_skb->sk);
Thomas Grafab364a62006-08-22 00:01:47 -07004909 struct nlattr *tb[RTA_MAX+1];
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004910 int err, iif = 0, oif = 0;
David Aherna68886a2018-04-20 15:38:02 -07004911 struct fib6_info *from;
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004912 struct dst_entry *dst;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004913 struct rt6_info *rt;
Thomas Grafab364a62006-08-22 00:01:47 -07004914 struct sk_buff *skb;
4915 struct rtmsg *rtm;
Maciej Żenczykowski744486d2018-09-29 23:44:54 -07004916 struct flowi6 fl6 = {};
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004917 bool fibmatch;
Thomas Grafab364a62006-08-22 00:01:47 -07004918
Jakub Kicinski0eff0a22019-01-18 10:46:24 -08004919 err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack);
Thomas Grafab364a62006-08-22 00:01:47 -07004920 if (err < 0)
4921 goto errout;
4922
4923 err = -EINVAL;
Hannes Frederic Sowa38b70972016-06-11 20:08:19 +02004924 rtm = nlmsg_data(nlh);
4925 fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004926 fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH);
Thomas Grafab364a62006-08-22 00:01:47 -07004927
4928 if (tb[RTA_SRC]) {
4929 if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr))
4930 goto errout;
4931
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004932 fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]);
Thomas Grafab364a62006-08-22 00:01:47 -07004933 }
4934
4935 if (tb[RTA_DST]) {
4936 if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr))
4937 goto errout;
4938
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00004939 fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]);
Thomas Grafab364a62006-08-22 00:01:47 -07004940 }
4941
4942 if (tb[RTA_IIF])
4943 iif = nla_get_u32(tb[RTA_IIF]);
4944
4945 if (tb[RTA_OIF])
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004946 oif = nla_get_u32(tb[RTA_OIF]);
Thomas Grafab364a62006-08-22 00:01:47 -07004947
Lorenzo Colitti2e47b292014-05-15 16:38:41 -07004948 if (tb[RTA_MARK])
4949 fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
4950
Lorenzo Colitti622ec2c2016-11-04 02:23:42 +09004951 if (tb[RTA_UID])
4952 fl6.flowi6_uid = make_kuid(current_user_ns(),
4953 nla_get_u32(tb[RTA_UID]));
4954 else
4955 fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
4956
Roopa Prabhueacb9382018-05-22 14:03:28 -07004957 if (tb[RTA_SPORT])
4958 fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
4959
4960 if (tb[RTA_DPORT])
4961 fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
4962
4963 if (tb[RTA_IP_PROTO]) {
4964 err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
Hangbin Liu5e1a99e2019-02-27 16:15:29 +08004965 &fl6.flowi6_proto, AF_INET6,
4966 extack);
Roopa Prabhueacb9382018-05-22 14:03:28 -07004967 if (err)
4968 goto errout;
4969 }
4970
Thomas Grafab364a62006-08-22 00:01:47 -07004971 if (iif) {
4972 struct net_device *dev;
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004973 int flags = 0;
4974
Florian Westphal121622d2017-08-15 16:34:42 +02004975 rcu_read_lock();
4976
4977 dev = dev_get_by_index_rcu(net, iif);
Thomas Grafab364a62006-08-22 00:01:47 -07004978 if (!dev) {
Florian Westphal121622d2017-08-15 16:34:42 +02004979 rcu_read_unlock();
Thomas Grafab364a62006-08-22 00:01:47 -07004980 err = -ENODEV;
4981 goto errout;
4982 }
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004983
4984 fl6.flowi6_iif = iif;
4985
4986 if (!ipv6_addr_any(&fl6.saddr))
4987 flags |= RT6_LOOKUP_F_HAS_SADDR;
4988
David Ahernb75cc8f2018-03-02 08:32:17 -08004989 dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags);
Florian Westphal121622d2017-08-15 16:34:42 +02004990
4991 rcu_read_unlock();
Shmulik Ladkani72331bc2012-04-01 04:03:45 +00004992 } else {
4993 fl6.flowi6_oif = oif;
4994
Ido Schimmel58acfd72017-12-20 12:28:25 +02004995 dst = ip6_route_output(net, NULL, &fl6);
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004996 }
4997
Roopa Prabhu18c3a612017-05-25 10:42:40 -07004998
4999 rt = container_of(dst, struct rt6_info, dst);
5000 if (rt->dst.error) {
5001 err = rt->dst.error;
5002 ip6_rt_put(rt);
5003 goto errout;
Thomas Grafab364a62006-08-22 00:01:47 -07005004 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005005
WANG Cong9d6acb32017-03-01 20:48:39 -08005006 if (rt == net->ipv6.ip6_null_entry) {
5007 err = rt->dst.error;
5008 ip6_rt_put(rt);
5009 goto errout;
5010 }
5011
Linus Torvalds1da177e2005-04-16 15:20:36 -07005012 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
David S. Miller38308472011-12-03 18:02:47 -05005013 if (!skb) {
Amerigo Wang94e187c2012-10-29 00:13:19 +00005014 ip6_rt_put(rt);
Thomas Grafab364a62006-08-22 00:01:47 -07005015 err = -ENOBUFS;
5016 goto errout;
5017 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005018
Changli Gaod8d1f302010-06-10 23:31:35 -07005019 skb_dst_set(skb, &rt->dst);
David Aherna68886a2018-04-20 15:38:02 -07005020
5021 rcu_read_lock();
5022 from = rcu_dereference(rt->from);
5023
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005024 if (fibmatch)
David Aherna68886a2018-04-20 15:38:02 -07005025 err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
Roopa Prabhu18c3a612017-05-25 10:42:40 -07005026 RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
5027 nlh->nlmsg_seq, 0);
5028 else
David Aherna68886a2018-04-20 15:38:02 -07005029 err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
5030 &fl6.saddr, iif, RTM_NEWROUTE,
David Ahernd4ead6b2018-04-17 17:33:16 -07005031 NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
5032 0);
David Aherna68886a2018-04-20 15:38:02 -07005033 rcu_read_unlock();
5034
Linus Torvalds1da177e2005-04-16 15:20:36 -07005035 if (err < 0) {
Thomas Grafab364a62006-08-22 00:01:47 -07005036 kfree_skb(skb);
5037 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005038 }
5039
Eric W. Biederman15e47302012-09-07 20:12:54 +00005040 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
Thomas Grafab364a62006-08-22 00:01:47 -07005041errout:
Linus Torvalds1da177e2005-04-16 15:20:36 -07005042 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005043}
5044
David Ahern8d1c8022018-04-17 17:33:26 -07005045void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
Roopa Prabhu37a1d362015-09-13 10:18:33 -07005046 unsigned int nlm_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005047{
5048 struct sk_buff *skb;
Daniel Lezcano55786892008-03-04 13:47:47 -08005049 struct net *net = info->nl_net;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005050 u32 seq;
5051 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08005053 err = -ENOBUFS;
David S. Miller38308472011-12-03 18:02:47 -05005054 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
Thomas Graf86872cb2006-08-22 00:01:08 -07005055
Roopa Prabhu19e42e42015-07-21 10:43:48 +02005056 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
David S. Miller38308472011-12-03 18:02:47 -05005057 if (!skb)
Thomas Graf21713eb2006-08-15 00:35:24 -07005058 goto errout;
5059
David Ahernd4ead6b2018-04-17 17:33:16 -07005060 err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
5061 event, info->portid, seq, nlm_flags);
Patrick McHardy26932562007-01-31 23:16:40 -08005062 if (err < 0) {
5063 /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
5064 WARN_ON(err == -EMSGSIZE);
5065 kfree_skb(skb);
5066 goto errout;
5067 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00005068 rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08005069 info->nlh, gfp_any());
5070 return;
Thomas Graf21713eb2006-08-15 00:35:24 -07005071errout:
5072 if (err < 0)
Daniel Lezcano55786892008-03-04 13:47:47 -08005073 rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005074}
5075
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005076static int ip6_route_dev_notify(struct notifier_block *this,
Jiri Pirko351638e2013-05-28 01:30:21 +00005077 unsigned long event, void *ptr)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005078{
Jiri Pirko351638e2013-05-28 01:30:21 +00005079 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09005080 struct net *net = dev_net(dev);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005081
WANG Cong242d3a42017-05-08 10:12:13 -07005082 if (!(dev->flags & IFF_LOOPBACK))
5083 return NOTIFY_OK;
5084
5085 if (event == NETDEV_REGISTER) {
David Ahernad1601a2019-03-27 20:53:56 -07005086 net->ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = dev;
Changli Gaod8d1f302010-06-10 23:31:35 -07005087 net->ipv6.ip6_null_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005088 net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
5089#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Changli Gaod8d1f302010-06-10 23:31:35 -07005090 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005091 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
Changli Gaod8d1f302010-06-10 23:31:35 -07005092 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005093 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
5094#endif
WANG Cong76da0702017-06-20 11:42:27 -07005095 } else if (event == NETDEV_UNREGISTER &&
5096 dev->reg_state != NETREG_UNREGISTERED) {
5097 /* NETDEV_UNREGISTER could be fired for multiple times by
5098 * netdev_wait_allrefs(). Make sure we only call this once.
5099 */
Eric Dumazet12d94a82017-08-15 04:09:51 -07005100 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005101#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Eric Dumazet12d94a82017-08-15 04:09:51 -07005102 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
5103 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
WANG Cong242d3a42017-05-08 10:12:13 -07005104#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005105 }
5106
5107 return NOTIFY_OK;
5108}
5109
Linus Torvalds1da177e2005-04-16 15:20:36 -07005110/*
5111 * /proc
5112 */
5113
5114#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07005115static int rt6_stats_seq_show(struct seq_file *seq, void *v)
5116{
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005117 struct net *net = (struct net *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005118 seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n",
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005119 net->ipv6.rt6_stats->fib_nodes,
5120 net->ipv6.rt6_stats->fib_route_nodes,
Wei Wang81eb8442017-10-06 12:06:11 -07005121 atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005122 net->ipv6.rt6_stats->fib_rt_entries,
5123 net->ipv6.rt6_stats->fib_rt_cache,
Eric Dumazetfc66f952010-10-08 06:37:34 +00005124 dst_entries_get_slow(&net->ipv6.ip6_dst_ops),
Daniel Lezcano69ddb802008-03-04 13:46:23 -08005125 net->ipv6.rt6_stats->fib_discarded_routes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005126
5127 return 0;
5128}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129#endif /* CONFIG_PROC_FS */
5130
5131#ifdef CONFIG_SYSCTL
5132
Linus Torvalds1da177e2005-04-16 15:20:36 -07005133static
Joe Perchesfe2c6332013-06-11 23:04:25 -07005134int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005135 void __user *buffer, size_t *lenp, loff_t *ppos)
5136{
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005137 struct net *net;
5138 int delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005139 int ret;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005140 if (!write)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005141 return -EINVAL;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005142
5143 net = (struct net *)ctl->extra1;
5144 delay = net->ipv6.sysctl.flush_delay;
Aditya Pakkif0fb9b22018-12-24 10:30:17 -06005145 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5146 if (ret)
5147 return ret;
5148
Michal Kubeček2ac3ac82013-08-01 10:04:14 +02005149 fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005150 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005151}
5152
David Ahern7c6bb7d2018-10-11 20:17:21 -07005153static int zero;
5154static int one = 1;
5155
David Aherned792e22018-10-08 14:06:34 -07005156static struct ctl_table ipv6_route_table_template[] = {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09005157 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005158 .procname = "flush",
Daniel Lezcano49905092008-01-10 03:01:01 -08005159 .data = &init_net.ipv6.sysctl.flush_delay,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005160 .maxlen = sizeof(int),
Dave Jones89c8b3a12005-04-28 12:11:49 -07005161 .mode = 0200,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005162 .proc_handler = ipv6_sysctl_rtcache_flush
Linus Torvalds1da177e2005-04-16 15:20:36 -07005163 },
5164 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005165 .procname = "gc_thresh",
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005166 .data = &ip6_dst_ops_template.gc_thresh,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005167 .maxlen = sizeof(int),
5168 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005169 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170 },
5171 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005172 .procname = "max_size",
Daniel Lezcano49905092008-01-10 03:01:01 -08005173 .data = &init_net.ipv6.sysctl.ip6_rt_max_size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174 .maxlen = sizeof(int),
5175 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005176 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005177 },
5178 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005179 .procname = "gc_min_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005180 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005181 .maxlen = sizeof(int),
5182 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005183 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005184 },
5185 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005186 .procname = "gc_timeout",
Daniel Lezcano49905092008-01-10 03:01:01 -08005187 .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005188 .maxlen = sizeof(int),
5189 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005190 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191 },
5192 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005193 .procname = "gc_interval",
Daniel Lezcano49905092008-01-10 03:01:01 -08005194 .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005195 .maxlen = sizeof(int),
5196 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005197 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005198 },
5199 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005200 .procname = "gc_elasticity",
Daniel Lezcano49905092008-01-10 03:01:01 -08005201 .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005202 .maxlen = sizeof(int),
5203 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005204 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005205 },
5206 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005207 .procname = "mtu_expires",
Daniel Lezcano49905092008-01-10 03:01:01 -08005208 .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209 .maxlen = sizeof(int),
5210 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005211 .proc_handler = proc_dointvec_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005212 },
5213 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005214 .procname = "min_adv_mss",
Daniel Lezcano49905092008-01-10 03:01:01 -08005215 .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005216 .maxlen = sizeof(int),
5217 .mode = 0644,
Min Zhangf3d3f612010-08-14 22:42:51 -07005218 .proc_handler = proc_dointvec,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219 },
5220 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005221 .procname = "gc_min_interval_ms",
Daniel Lezcano49905092008-01-10 03:01:01 -08005222 .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005223 .maxlen = sizeof(int),
5224 .mode = 0644,
Alexey Dobriyan6d9f2392008-11-03 18:21:05 -08005225 .proc_handler = proc_dointvec_ms_jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 },
David Ahern7c6bb7d2018-10-11 20:17:21 -07005227 {
5228 .procname = "skip_notify_on_dev_down",
5229 .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down,
5230 .maxlen = sizeof(int),
5231 .mode = 0644,
5232 .proc_handler = proc_dointvec,
5233 .extra1 = &zero,
5234 .extra2 = &one,
5235 },
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08005236 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005237};
5238
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005239struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005240{
5241 struct ctl_table *table;
5242
5243 table = kmemdup(ipv6_route_table_template,
5244 sizeof(ipv6_route_table_template),
5245 GFP_KERNEL);
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005246
5247 if (table) {
5248 table[0].data = &net->ipv6.sysctl.flush_delay;
Lucian Adrian Grijincuc486da32011-02-24 19:48:03 +00005249 table[0].extra1 = net;
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005250 table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005251 table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
5252 table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
5253 table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
5254 table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
5255 table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity;
5256 table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
5257 table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
Alexey Dobriyan9c69fab2009-12-18 20:11:03 -08005258 table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005259 table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down;
Eric W. Biederman464dc802012-11-16 03:02:59 +00005260
5261 /* Don't export sysctls to unprivileged users */
5262 if (net->user_ns != &init_user_ns)
5263 table[0].procname = NULL;
YOSHIFUJI Hideaki5ee09102008-02-28 00:24:28 +09005264 }
5265
Daniel Lezcano760f2d02008-01-10 02:53:43 -08005266 return table;
5267}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005268#endif
5269
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005270static int __net_init ip6_route_net_init(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005271{
Pavel Emelyanov633d424b2008-04-21 14:25:23 -07005272 int ret = -ENOMEM;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005273
Alexey Dobriyan86393e52009-08-29 01:34:49 +00005274 memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
5275 sizeof(net->ipv6.ip6_dst_ops));
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005276
Eric Dumazetfc66f952010-10-08 06:37:34 +00005277 if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
5278 goto out_ip6_dst_ops;
5279
David Ahern421842e2018-04-17 17:33:18 -07005280 net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
5281 sizeof(*net->ipv6.fib6_null_entry),
5282 GFP_KERNEL);
5283 if (!net->ipv6.fib6_null_entry)
5284 goto out_ip6_dst_entries;
5285
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005286 net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
5287 sizeof(*net->ipv6.ip6_null_entry),
5288 GFP_KERNEL);
5289 if (!net->ipv6.ip6_null_entry)
David Ahern421842e2018-04-17 17:33:18 -07005290 goto out_fib6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005291 net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005292 dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
5293 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005294
5295#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Vincent Bernatfeca7d82017-08-08 20:23:49 +02005296 net->ipv6.fib6_has_custom_rules = false;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005297 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
5298 sizeof(*net->ipv6.ip6_prohibit_entry),
5299 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005300 if (!net->ipv6.ip6_prohibit_entry)
5301 goto out_ip6_null_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005302 net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005303 dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst,
5304 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005305
5306 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
5307 sizeof(*net->ipv6.ip6_blk_hole_entry),
5308 GFP_KERNEL);
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005309 if (!net->ipv6.ip6_blk_hole_entry)
5310 goto out_ip6_prohibit_entry;
Changli Gaod8d1f302010-06-10 23:31:35 -07005311 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
David S. Miller62fa8a82011-01-26 20:51:05 -08005312 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
5313 ip6_template_metrics, true);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005314#endif
5315
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005316 net->ipv6.sysctl.flush_delay = 0;
5317 net->ipv6.sysctl.ip6_rt_max_size = 4096;
5318 net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2;
5319 net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ;
5320 net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;
5321 net->ipv6.sysctl.ip6_rt_gc_elasticity = 9;
5322 net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ;
5323 net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;
David Ahern7c6bb7d2018-10-11 20:17:21 -07005324 net->ipv6.sysctl.skip_notify_on_dev_down = 0;
Peter Zijlstrab339a47c2008-10-07 14:15:00 -07005325
Benjamin Thery6891a342008-03-04 13:49:47 -08005326 net->ipv6.ip6_rt_gc_expire = 30*HZ;
5327
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005328 ret = 0;
5329out:
5330 return ret;
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005331
Peter Zijlstra68fffc62008-10-07 14:12:10 -07005332#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5333out_ip6_prohibit_entry:
5334 kfree(net->ipv6.ip6_prohibit_entry);
5335out_ip6_null_entry:
5336 kfree(net->ipv6.ip6_null_entry);
5337#endif
David Ahern421842e2018-04-17 17:33:18 -07005338out_fib6_null_entry:
5339 kfree(net->ipv6.fib6_null_entry);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005340out_ip6_dst_entries:
5341 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005342out_ip6_dst_ops:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005343 goto out;
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005344}
5345
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00005346static void __net_exit ip6_route_net_exit(struct net *net)
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005347{
David Ahern421842e2018-04-17 17:33:18 -07005348 kfree(net->ipv6.fib6_null_entry);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005349 kfree(net->ipv6.ip6_null_entry);
5350#ifdef CONFIG_IPV6_MULTIPLE_TABLES
5351 kfree(net->ipv6.ip6_prohibit_entry);
5352 kfree(net->ipv6.ip6_blk_hole_entry);
5353#endif
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005354 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005355}
5356
Thomas Grafd1896342012-06-18 12:08:33 +00005357static int __net_init ip6_route_net_init_late(struct net *net)
5358{
5359#ifdef CONFIG_PROC_FS
Christoph Hellwigc3506372018-04-10 19:42:55 +02005360 proc_create_net("ipv6_route", 0, net->proc_net, &ipv6_route_seq_ops,
5361 sizeof(struct ipv6_route_iter));
Christoph Hellwig3617d942018-04-13 20:38:35 +02005362 proc_create_net_single("rt6_stats", 0444, net->proc_net,
5363 rt6_stats_seq_show, NULL);
Thomas Grafd1896342012-06-18 12:08:33 +00005364#endif
5365 return 0;
5366}
5367
5368static void __net_exit ip6_route_net_exit_late(struct net *net)
5369{
5370#ifdef CONFIG_PROC_FS
Gao fengece31ff2013-02-18 01:34:56 +00005371 remove_proc_entry("ipv6_route", net->proc_net);
5372 remove_proc_entry("rt6_stats", net->proc_net);
Thomas Grafd1896342012-06-18 12:08:33 +00005373#endif
5374}
5375
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005376static struct pernet_operations ip6_route_net_ops = {
5377 .init = ip6_route_net_init,
5378 .exit = ip6_route_net_exit,
5379};
5380
David S. Millerc3426b42012-06-09 16:27:05 -07005381static int __net_init ipv6_inetpeer_init(struct net *net)
5382{
5383 struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL);
5384
5385 if (!bp)
5386 return -ENOMEM;
5387 inet_peer_base_init(bp);
5388 net->ipv6.peers = bp;
5389 return 0;
5390}
5391
5392static void __net_exit ipv6_inetpeer_exit(struct net *net)
5393{
5394 struct inet_peer_base *bp = net->ipv6.peers;
5395
5396 net->ipv6.peers = NULL;
David S. Miller56a6b242012-06-09 16:32:41 -07005397 inetpeer_invalidate_tree(bp);
David S. Millerc3426b42012-06-09 16:27:05 -07005398 kfree(bp);
5399}
5400
David S. Miller2b823f72012-06-09 19:00:16 -07005401static struct pernet_operations ipv6_inetpeer_ops = {
David S. Millerc3426b42012-06-09 16:27:05 -07005402 .init = ipv6_inetpeer_init,
5403 .exit = ipv6_inetpeer_exit,
5404};
5405
Thomas Grafd1896342012-06-18 12:08:33 +00005406static struct pernet_operations ip6_route_net_late_ops = {
5407 .init = ip6_route_net_init_late,
5408 .exit = ip6_route_net_exit_late,
5409};
5410
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005411static struct notifier_block ip6_route_dev_notifier = {
5412 .notifier_call = ip6_route_dev_notify,
WANG Cong242d3a42017-05-08 10:12:13 -07005413 .priority = ADDRCONF_NOTIFY_PRIORITY - 10,
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005414};
5415
WANG Cong2f460932017-05-03 22:07:31 -07005416void __init ip6_route_init_special_entries(void)
5417{
5418 /* Registering of the loopback is done before this portion of code,
5419 * the loopback reference in rt6_info will not be taken, do it
5420 * manually for init_net */
David Ahernad1601a2019-03-27 20:53:56 -07005421 init_net.ipv6.fib6_null_entry->fib6_nh.fib_nh_dev = init_net.loopback_dev;
WANG Cong2f460932017-05-03 22:07:31 -07005422 init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
5423 init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5424 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
5425 init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev;
5426 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5427 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
5428 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
5429 #endif
5430}
5431
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005432int __init ip6_route_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005433{
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005434 int ret;
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005435 int cpu;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005436
Daniel Lezcano9a7ec3a2008-03-04 13:48:53 -08005437 ret = -ENOMEM;
5438 ip6_dst_ops_template.kmem_cachep =
5439 kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
5440 SLAB_HWCACHE_ALIGN, NULL);
5441 if (!ip6_dst_ops_template.kmem_cachep)
Fernando Carrijoc19a28e2009-01-07 18:09:08 -08005442 goto out;
David S. Miller14e50e52007-05-24 18:17:54 -07005443
Eric Dumazetfc66f952010-10-08 06:37:34 +00005444 ret = dst_entries_init(&ip6_dst_blackhole_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005445 if (ret)
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005446 goto out_kmem_cache;
Daniel Lezcanobdb32892008-03-04 13:48:10 -08005447
David S. Millerc3426b42012-06-09 16:27:05 -07005448 ret = register_pernet_subsys(&ipv6_inetpeer_ops);
5449 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005450 goto out_dst_entries;
Thomas Graf2a0c4512012-06-14 23:00:17 +00005451
David S. Miller7e52b332012-06-15 15:51:55 -07005452 ret = register_pernet_subsys(&ip6_route_net_ops);
5453 if (ret)
5454 goto out_register_inetpeer;
David S. Millerc3426b42012-06-09 16:27:05 -07005455
Arnaud Ebalard5dc121e2008-10-01 02:37:56 -07005456 ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep;
5457
David S. Millere8803b62012-06-16 01:12:19 -07005458 ret = fib6_init();
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005459 if (ret)
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005460 goto out_register_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005461
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005462 ret = xfrm6_init();
5463 if (ret)
David S. Millere8803b62012-06-16 01:12:19 -07005464 goto out_fib6_init;
Daniel Lezcanoc35b7e72007-12-08 00:14:11 -08005465
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005466 ret = fib6_rules_init();
5467 if (ret)
5468 goto xfrm6_init;
Daniel Lezcano7e5449c2007-12-08 00:14:54 -08005469
Thomas Grafd1896342012-06-18 12:08:33 +00005470 ret = register_pernet_subsys(&ip6_route_net_late_ops);
5471 if (ret)
5472 goto fib6_rules_init;
5473
Florian Westphal16feebc2017-12-02 21:44:08 +01005474 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
5475 inet6_rtm_newroute, NULL, 0);
5476 if (ret < 0)
5477 goto out_register_late_subsys;
5478
5479 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
5480 inet6_rtm_delroute, NULL, 0);
5481 if (ret < 0)
5482 goto out_register_late_subsys;
5483
5484 ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
5485 inet6_rtm_getroute, NULL,
5486 RTNL_FLAG_DOIT_UNLOCKED);
5487 if (ret < 0)
Thomas Grafd1896342012-06-18 12:08:33 +00005488 goto out_register_late_subsys;
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005489
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005490 ret = register_netdevice_notifier(&ip6_route_dev_notifier);
Daniel Lezcanocdb18762008-03-04 13:45:33 -08005491 if (ret)
Thomas Grafd1896342012-06-18 12:08:33 +00005492 goto out_register_late_subsys;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005493
Martin KaFai Lau8d0b94a2015-05-22 20:56:04 -07005494 for_each_possible_cpu(cpu) {
5495 struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
5496
5497 INIT_LIST_HEAD(&ul->head);
5498 spin_lock_init(&ul->lock);
5499 }
5500
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005501out:
5502 return ret;
5503
Thomas Grafd1896342012-06-18 12:08:33 +00005504out_register_late_subsys:
Florian Westphal16feebc2017-12-02 21:44:08 +01005505 rtnl_unregister_all(PF_INET6);
Thomas Grafd1896342012-06-18 12:08:33 +00005506 unregister_pernet_subsys(&ip6_route_net_late_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005507fib6_rules_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005508 fib6_rules_cleanup();
5509xfrm6_init:
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005510 xfrm6_fini();
Thomas Graf2a0c4512012-06-14 23:00:17 +00005511out_fib6_init:
5512 fib6_gc_cleanup();
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005513out_register_subsys:
5514 unregister_pernet_subsys(&ip6_route_net_ops);
David S. Miller7e52b332012-06-15 15:51:55 -07005515out_register_inetpeer:
5516 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Eric Dumazetfc66f952010-10-08 06:37:34 +00005517out_dst_entries:
5518 dst_entries_destroy(&ip6_dst_blackhole_ops);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005519out_kmem_cache:
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005520 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Daniel Lezcano433d49c2007-12-07 00:43:48 -08005521 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005522}
5523
5524void ip6_route_cleanup(void)
5525{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005526 unregister_netdevice_notifier(&ip6_route_dev_notifier);
Thomas Grafd1896342012-06-18 12:08:33 +00005527 unregister_pernet_subsys(&ip6_route_net_late_ops);
Thomas Graf101367c2006-08-04 03:39:02 -07005528 fib6_rules_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005529 xfrm6_fini();
Linus Torvalds1da177e2005-04-16 15:20:36 -07005530 fib6_gc_cleanup();
David S. Millerc3426b42012-06-09 16:27:05 -07005531 unregister_pernet_subsys(&ipv6_inetpeer_ops);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08005532 unregister_pernet_subsys(&ip6_route_net_ops);
Xiaotian Feng41bb78b2010-11-02 16:11:05 +00005533 dst_entries_destroy(&ip6_dst_blackhole_ops);
Benjamin Theryf2fc6a52008-03-04 13:49:23 -08005534 kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535}