18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux INET6 implementation 48c2ecf20Sopenharmony_ci * FIB front-end. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* Changes: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI 138c2ecf20Sopenharmony_ci * reworked default router selection. 148c2ecf20Sopenharmony_ci * - respect outgoing interface 158c2ecf20Sopenharmony_ci * - select from (probably) reachable routers (i.e. 168c2ecf20Sopenharmony_ci * routers in REACHABLE, STALE, DELAY or PROBE states). 178c2ecf20Sopenharmony_ci * - always select the same router if it is (probably) 188c2ecf20Sopenharmony_ci * reachable. otherwise, round-robin the list. 198c2ecf20Sopenharmony_ci * Ville Nuorvala 208c2ecf20Sopenharmony_ci * Fixed routing subtrees. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/capability.h> 268c2ecf20Sopenharmony_ci#include <linux/errno.h> 278c2ecf20Sopenharmony_ci#include <linux/export.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/times.h> 308c2ecf20Sopenharmony_ci#include <linux/socket.h> 318c2ecf20Sopenharmony_ci#include <linux/sockios.h> 328c2ecf20Sopenharmony_ci#include <linux/net.h> 338c2ecf20Sopenharmony_ci#include <linux/route.h> 348c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 358c2ecf20Sopenharmony_ci#include <linux/in6.h> 368c2ecf20Sopenharmony_ci#include <linux/mroute6.h> 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 398c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 408c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 418c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 428c2ecf20Sopenharmony_ci#include <linux/slab.h> 438c2ecf20Sopenharmony_ci#include <linux/jhash.h> 448c2ecf20Sopenharmony_ci#include <linux/siphash.h> 458c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 468c2ecf20Sopenharmony_ci#include <net/snmp.h> 478c2ecf20Sopenharmony_ci#include <net/ipv6.h> 488c2ecf20Sopenharmony_ci#include <net/ip6_fib.h> 498c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 508c2ecf20Sopenharmony_ci#include <net/ndisc.h> 518c2ecf20Sopenharmony_ci#include <net/addrconf.h> 528c2ecf20Sopenharmony_ci#include <net/tcp.h> 538c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 548c2ecf20Sopenharmony_ci#include <net/dst.h> 558c2ecf20Sopenharmony_ci#include <net/dst_metadata.h> 568c2ecf20Sopenharmony_ci#include <net/xfrm.h> 578c2ecf20Sopenharmony_ci#include <net/netevent.h> 588c2ecf20Sopenharmony_ci#include <net/netlink.h> 598c2ecf20Sopenharmony_ci#include <net/rtnh.h> 608c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 618c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 628c2ecf20Sopenharmony_ci#include <net/l3mdev.h> 638c2ecf20Sopenharmony_ci#include <net/ip.h> 648c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 658c2ecf20Sopenharmony_ci#include <linux/btf_ids.h> 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 688c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 748c2ecf20Sopenharmony_ci#include <trace/events/fib6.h> 758c2ecf20Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup); 768c2ecf20Sopenharmony_ci#undef CREATE_TRACE_POINTS 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cienum rt6_nud_state { 798c2ecf20Sopenharmony_ci RT6_NUD_FAIL_HARD = -3, 808c2ecf20Sopenharmony_ci RT6_NUD_FAIL_PROBE = -2, 818c2ecf20Sopenharmony_ci RT6_NUD_FAIL_DO_RR = -1, 828c2ecf20Sopenharmony_ci RT6_NUD_SUCCEED = 1 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 868c2ecf20Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst); 878c2ecf20Sopenharmony_cistatic unsigned int ip6_mtu(const struct dst_entry *dst); 888c2ecf20Sopenharmony_cistatic void ip6_negative_advice(struct sock *sk, 898c2ecf20Sopenharmony_ci struct dst_entry *dst); 908c2ecf20Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *); 918c2ecf20Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *, 928c2ecf20Sopenharmony_ci struct net_device *dev, int how); 938c2ecf20Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb); 968c2ecf20Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); 978c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb); 988c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); 998c2ecf20Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb); 1008c2ecf20Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 1018c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 mtu, 1028c2ecf20Sopenharmony_ci bool confirm_neigh); 1038c2ecf20Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 1048c2ecf20Sopenharmony_ci struct sk_buff *skb); 1058c2ecf20Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif, 1068c2ecf20Sopenharmony_ci int strict); 1078c2ecf20Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i); 1088c2ecf20Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb, 1098c2ecf20Sopenharmony_ci struct fib6_info *rt, struct dst_entry *dst, 1108c2ecf20Sopenharmony_ci struct in6_addr *dest, struct in6_addr *src, 1118c2ecf20Sopenharmony_ci int iif, int type, u32 portid, u32 seq, 1128c2ecf20Sopenharmony_ci unsigned int flags); 1138c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 1148c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 1158c2ecf20Sopenharmony_ci const struct in6_addr *saddr); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 1188c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net, 1198c2ecf20Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 1208c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr, 1218c2ecf20Sopenharmony_ci struct net_device *dev, 1228c2ecf20Sopenharmony_ci unsigned int pref); 1238c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net, 1248c2ecf20Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 1258c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr, 1268c2ecf20Sopenharmony_ci struct net_device *dev); 1278c2ecf20Sopenharmony_ci#endif 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistruct uncached_list { 1308c2ecf20Sopenharmony_ci spinlock_t lock; 1318c2ecf20Sopenharmony_ci struct list_head head; 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_civoid rt6_uncached_list_add(struct rt6_info *rt) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci rt->rt6i_uncached_list = ul; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 1438c2ecf20Sopenharmony_ci list_add_tail(&rt->rt6i_uncached, &ul->head); 1448c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid rt6_uncached_list_del(struct rt6_info *rt) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci if (!list_empty(&rt->rt6i_uncached)) { 1508c2ecf20Sopenharmony_ci struct uncached_list *ul = rt->rt6i_uncached_list; 1518c2ecf20Sopenharmony_ci struct net *net = dev_net(rt->dst.dev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 1548c2ecf20Sopenharmony_ci list_del(&rt->rt6i_uncached); 1558c2ecf20Sopenharmony_ci atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache); 1568c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct net_device *loopback_dev = net->loopback_dev; 1638c2ecf20Sopenharmony_ci int cpu; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (dev == loopback_dev) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 1698c2ecf20Sopenharmony_ci struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1708c2ecf20Sopenharmony_ci struct rt6_info *rt; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 1738c2ecf20Sopenharmony_ci list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1748c2ecf20Sopenharmony_ci struct inet6_dev *rt_idev = rt->rt6i_idev; 1758c2ecf20Sopenharmony_ci struct net_device *rt_dev = rt->dst.dev; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (rt_idev->dev == dev) { 1788c2ecf20Sopenharmony_ci rt->rt6i_idev = in6_dev_get(loopback_dev); 1798c2ecf20Sopenharmony_ci in6_dev_put(rt_idev); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (rt_dev == dev) { 1838c2ecf20Sopenharmony_ci rt->dst.dev = blackhole_netdev; 1848c2ecf20Sopenharmony_ci dev_hold(rt->dst.dev); 1858c2ecf20Sopenharmony_ci dev_put(rt_dev); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic inline const void *choose_neigh_daddr(const struct in6_addr *p, 1938c2ecf20Sopenharmony_ci struct sk_buff *skb, 1948c2ecf20Sopenharmony_ci const void *daddr) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci if (!ipv6_addr_any(p)) 1978c2ecf20Sopenharmony_ci return (const void *) p; 1988c2ecf20Sopenharmony_ci else if (skb) 1998c2ecf20Sopenharmony_ci return &ipv6_hdr(skb)->daddr; 2008c2ecf20Sopenharmony_ci return daddr; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistruct neighbour *ip6_neigh_lookup(const struct in6_addr *gw, 2048c2ecf20Sopenharmony_ci struct net_device *dev, 2058c2ecf20Sopenharmony_ci struct sk_buff *skb, 2068c2ecf20Sopenharmony_ci const void *daddr) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct neighbour *n; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci daddr = choose_neigh_daddr(gw, skb, daddr); 2118c2ecf20Sopenharmony_ci n = __ipv6_neigh_lookup(dev, daddr); 2128c2ecf20Sopenharmony_ci if (n) 2138c2ecf20Sopenharmony_ci return n; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci n = neigh_create(&nd_tbl, daddr, dev); 2168c2ecf20Sopenharmony_ci return IS_ERR(n) ? NULL : n; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst, 2208c2ecf20Sopenharmony_ci struct sk_buff *skb, 2218c2ecf20Sopenharmony_ci const void *daddr) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci const struct rt6_info *rt = container_of(dst, struct rt6_info, dst); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any), 2268c2ecf20Sopenharmony_ci dst->dev, skb, daddr); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct net_device *dev = dst->dev; 2328c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr); 2358c2ecf20Sopenharmony_ci if (!daddr) 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 2388c2ecf20Sopenharmony_ci return; 2398c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast((const struct in6_addr *)daddr)) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci __ipv6_confirm_neigh(dev, daddr); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic struct dst_ops ip6_dst_ops_template = { 2458c2ecf20Sopenharmony_ci .family = AF_INET6, 2468c2ecf20Sopenharmony_ci .gc = ip6_dst_gc, 2478c2ecf20Sopenharmony_ci .gc_thresh = 1024, 2488c2ecf20Sopenharmony_ci .check = ip6_dst_check, 2498c2ecf20Sopenharmony_ci .default_advmss = ip6_default_advmss, 2508c2ecf20Sopenharmony_ci .mtu = ip6_mtu, 2518c2ecf20Sopenharmony_ci .cow_metrics = dst_cow_metrics_generic, 2528c2ecf20Sopenharmony_ci .destroy = ip6_dst_destroy, 2538c2ecf20Sopenharmony_ci .ifdown = ip6_dst_ifdown, 2548c2ecf20Sopenharmony_ci .negative_advice = ip6_negative_advice, 2558c2ecf20Sopenharmony_ci .link_failure = ip6_link_failure, 2568c2ecf20Sopenharmony_ci .update_pmtu = ip6_rt_update_pmtu, 2578c2ecf20Sopenharmony_ci .redirect = rt6_do_redirect, 2588c2ecf20Sopenharmony_ci .local_out = __ip6_local_out, 2598c2ecf20Sopenharmony_ci .neigh_lookup = ip6_dst_neigh_lookup, 2608c2ecf20Sopenharmony_ci .confirm_neigh = ip6_confirm_neigh, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic struct dst_ops ip6_dst_blackhole_ops = { 2648c2ecf20Sopenharmony_ci .family = AF_INET6, 2658c2ecf20Sopenharmony_ci .default_advmss = ip6_default_advmss, 2668c2ecf20Sopenharmony_ci .neigh_lookup = ip6_dst_neigh_lookup, 2678c2ecf20Sopenharmony_ci .check = ip6_dst_check, 2688c2ecf20Sopenharmony_ci .destroy = ip6_dst_destroy, 2698c2ecf20Sopenharmony_ci .cow_metrics = dst_cow_metrics_generic, 2708c2ecf20Sopenharmony_ci .update_pmtu = dst_blackhole_update_pmtu, 2718c2ecf20Sopenharmony_ci .redirect = dst_blackhole_redirect, 2728c2ecf20Sopenharmony_ci .mtu = dst_blackhole_mtu, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const u32 ip6_template_metrics[RTAX_MAX] = { 2768c2ecf20Sopenharmony_ci [RTAX_HOPLIMIT - 1] = 0, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct fib6_info fib6_null_entry_template = { 2808c2ecf20Sopenharmony_ci .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP), 2818c2ecf20Sopenharmony_ci .fib6_protocol = RTPROT_KERNEL, 2828c2ecf20Sopenharmony_ci .fib6_metric = ~(u32)0, 2838c2ecf20Sopenharmony_ci .fib6_ref = REFCOUNT_INIT(1), 2848c2ecf20Sopenharmony_ci .fib6_type = RTN_UNREACHABLE, 2858c2ecf20Sopenharmony_ci .fib6_metrics = (struct dst_metrics *)&dst_default_metrics, 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_null_entry_template = { 2898c2ecf20Sopenharmony_ci .dst = { 2908c2ecf20Sopenharmony_ci .__refcnt = ATOMIC_INIT(1), 2918c2ecf20Sopenharmony_ci .__use = 1, 2928c2ecf20Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 2938c2ecf20Sopenharmony_ci .error = -ENETUNREACH, 2948c2ecf20Sopenharmony_ci .input = ip6_pkt_discard, 2958c2ecf20Sopenharmony_ci .output = ip6_pkt_discard_out, 2968c2ecf20Sopenharmony_ci }, 2978c2ecf20Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_prohibit_entry_template = { 3038c2ecf20Sopenharmony_ci .dst = { 3048c2ecf20Sopenharmony_ci .__refcnt = ATOMIC_INIT(1), 3058c2ecf20Sopenharmony_ci .__use = 1, 3068c2ecf20Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 3078c2ecf20Sopenharmony_ci .error = -EACCES, 3088c2ecf20Sopenharmony_ci .input = ip6_pkt_prohibit, 3098c2ecf20Sopenharmony_ci .output = ip6_pkt_prohibit_out, 3108c2ecf20Sopenharmony_ci }, 3118c2ecf20Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic const struct rt6_info ip6_blk_hole_entry_template = { 3158c2ecf20Sopenharmony_ci .dst = { 3168c2ecf20Sopenharmony_ci .__refcnt = ATOMIC_INIT(1), 3178c2ecf20Sopenharmony_ci .__use = 1, 3188c2ecf20Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 3198c2ecf20Sopenharmony_ci .error = -EINVAL, 3208c2ecf20Sopenharmony_ci .input = dst_discard, 3218c2ecf20Sopenharmony_ci .output = dst_discard_out, 3228c2ecf20Sopenharmony_ci }, 3238c2ecf20Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#endif 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void rt6_info_init(struct rt6_info *rt) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct dst_entry *dst = &rt->dst; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 3338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rt->rt6i_uncached); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* allocate dst with ip6_dst_ops */ 3378c2ecf20Sopenharmony_cistruct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, 3388c2ecf20Sopenharmony_ci int flags) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3418c2ecf20Sopenharmony_ci 1, DST_OBSOLETE_FORCE_CHK, flags); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (rt) { 3448c2ecf20Sopenharmony_ci rt6_info_init(rt); 3458c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return rt; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip6_dst_alloc); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *dst) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 3558c2ecf20Sopenharmony_ci struct fib6_info *from; 3568c2ecf20Sopenharmony_ci struct inet6_dev *idev; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ip_dst_metrics_put(dst); 3598c2ecf20Sopenharmony_ci rt6_uncached_list_del(rt); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci idev = rt->rt6i_idev; 3628c2ecf20Sopenharmony_ci if (idev) { 3638c2ecf20Sopenharmony_ci rt->rt6i_idev = NULL; 3648c2ecf20Sopenharmony_ci in6_dev_put(idev); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci from = xchg((__force struct fib6_info **)&rt->from, NULL); 3688c2ecf20Sopenharmony_ci fib6_info_release(from); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3728c2ecf20Sopenharmony_ci int how) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 3758c2ecf20Sopenharmony_ci struct inet6_dev *idev = rt->rt6i_idev; 3768c2ecf20Sopenharmony_ci struct net_device *loopback_dev = 3778c2ecf20Sopenharmony_ci dev_net(dev)->loopback_dev; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (idev && idev->dev != loopback_dev) { 3808c2ecf20Sopenharmony_ci struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); 3818c2ecf20Sopenharmony_ci if (loopback_idev) { 3828c2ecf20Sopenharmony_ci rt->rt6i_idev = loopback_idev; 3838c2ecf20Sopenharmony_ci in6_dev_put(idev); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic bool __rt6_check_expired(const struct rt6_info *rt) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_EXPIRES) 3918c2ecf20Sopenharmony_ci return time_after(jiffies, rt->dst.expires); 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci return false; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic bool rt6_check_expired(const struct rt6_info *rt) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct fib6_info *from; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_EXPIRES) { 4038c2ecf20Sopenharmony_ci if (time_after(jiffies, rt->dst.expires)) 4048c2ecf20Sopenharmony_ci return true; 4058c2ecf20Sopenharmony_ci } else if (from) { 4068c2ecf20Sopenharmony_ci return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK || 4078c2ecf20Sopenharmony_ci fib6_check_expired(from); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci return false; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_civoid fib6_select_path(const struct net *net, struct fib6_result *res, 4138c2ecf20Sopenharmony_ci struct flowi6 *fl6, int oif, bool have_oif_match, 4148c2ecf20Sopenharmony_ci const struct sk_buff *skb, int strict) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 4178c2ecf20Sopenharmony_ci struct fib6_info *match = res->f6i; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (!match->nh && (!match->fib6_nsiblings || have_oif_match)) 4208c2ecf20Sopenharmony_ci goto out; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (match->nh && have_oif_match && res->nh) 4238c2ecf20Sopenharmony_ci return; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* We might have already computed the hash for ICMPv6 errors. In such 4268c2ecf20Sopenharmony_ci * case it will always be non-zero. Otherwise now is the time to do it. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci if (!fl6->mp_hash && 4298c2ecf20Sopenharmony_ci (!match->nh || nexthop_is_multipath(match->nh))) 4308c2ecf20Sopenharmony_ci fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (unlikely(match->nh)) { 4338c2ecf20Sopenharmony_ci nexthop_path_fib6_result(res, fl6->mp_hash); 4348c2ecf20Sopenharmony_ci return; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound)) 4388c2ecf20Sopenharmony_ci goto out; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings, 4418c2ecf20Sopenharmony_ci fib6_siblings) { 4428c2ecf20Sopenharmony_ci const struct fib6_nh *nh = sibling->fib6_nh; 4438c2ecf20Sopenharmony_ci int nh_upper_bound; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound); 4468c2ecf20Sopenharmony_ci if (fl6->mp_hash > nh_upper_bound) 4478c2ecf20Sopenharmony_ci continue; 4488c2ecf20Sopenharmony_ci if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0) 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci match = sibling; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciout: 4558c2ecf20Sopenharmony_ci res->f6i = match; 4568c2ecf20Sopenharmony_ci res->nh = match->fib6_nh; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci * Route lookup. rcu_read_lock() should be held. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic bool __rt6_device_match(struct net *net, const struct fib6_nh *nh, 4648c2ecf20Sopenharmony_ci const struct in6_addr *saddr, int oif, int flags) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci const struct net_device *dev; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) 4698c2ecf20Sopenharmony_ci return false; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci dev = nh->fib_nh_dev; 4728c2ecf20Sopenharmony_ci if (oif) { 4738c2ecf20Sopenharmony_ci if (dev->ifindex == oif) 4748c2ecf20Sopenharmony_ci return true; 4758c2ecf20Sopenharmony_ci } else { 4768c2ecf20Sopenharmony_ci if (ipv6_chk_addr(net, saddr, dev, 4778c2ecf20Sopenharmony_ci flags & RT6_LOOKUP_F_IFACE)) 4788c2ecf20Sopenharmony_ci return true; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return false; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistruct fib6_nh_dm_arg { 4858c2ecf20Sopenharmony_ci struct net *net; 4868c2ecf20Sopenharmony_ci const struct in6_addr *saddr; 4878c2ecf20Sopenharmony_ci int oif; 4888c2ecf20Sopenharmony_ci int flags; 4898c2ecf20Sopenharmony_ci struct fib6_nh *nh; 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct fib6_nh_dm_arg *arg = _arg; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci arg->nh = nh; 4978c2ecf20Sopenharmony_ci return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif, 4988c2ecf20Sopenharmony_ci arg->flags); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* returns fib6_nh from nexthop or NULL */ 5028c2ecf20Sopenharmony_cistatic struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh, 5038c2ecf20Sopenharmony_ci struct fib6_result *res, 5048c2ecf20Sopenharmony_ci const struct in6_addr *saddr, 5058c2ecf20Sopenharmony_ci int oif, int flags) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct fib6_nh_dm_arg arg = { 5088c2ecf20Sopenharmony_ci .net = net, 5098c2ecf20Sopenharmony_ci .saddr = saddr, 5108c2ecf20Sopenharmony_ci .oif = oif, 5118c2ecf20Sopenharmony_ci .flags = flags, 5128c2ecf20Sopenharmony_ci }; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(nh)) 5158c2ecf20Sopenharmony_ci return NULL; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg)) 5188c2ecf20Sopenharmony_ci return arg.nh; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return NULL; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic void rt6_device_match(struct net *net, struct fib6_result *res, 5248c2ecf20Sopenharmony_ci const struct in6_addr *saddr, int oif, int flags) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 5278c2ecf20Sopenharmony_ci struct fib6_info *spf6i; 5288c2ecf20Sopenharmony_ci struct fib6_nh *nh; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!oif && ipv6_addr_any(saddr)) { 5318c2ecf20Sopenharmony_ci if (unlikely(f6i->nh)) { 5328c2ecf20Sopenharmony_ci nh = nexthop_fib6_nh(f6i->nh); 5338c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) 5348c2ecf20Sopenharmony_ci goto out_blackhole; 5358c2ecf20Sopenharmony_ci } else { 5368c2ecf20Sopenharmony_ci nh = f6i->fib6_nh; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci if (!(nh->fib_nh_flags & RTNH_F_DEAD)) 5398c2ecf20Sopenharmony_ci goto out; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) { 5438c2ecf20Sopenharmony_ci bool matched = false; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (unlikely(spf6i->nh)) { 5468c2ecf20Sopenharmony_ci nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr, 5478c2ecf20Sopenharmony_ci oif, flags); 5488c2ecf20Sopenharmony_ci if (nh) 5498c2ecf20Sopenharmony_ci matched = true; 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci nh = spf6i->fib6_nh; 5528c2ecf20Sopenharmony_ci if (__rt6_device_match(net, nh, saddr, oif, flags)) 5538c2ecf20Sopenharmony_ci matched = true; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci if (matched) { 5568c2ecf20Sopenharmony_ci res->f6i = spf6i; 5578c2ecf20Sopenharmony_ci goto out; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (oif && flags & RT6_LOOKUP_F_IFACE) { 5628c2ecf20Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 5638c2ecf20Sopenharmony_ci nh = res->f6i->fib6_nh; 5648c2ecf20Sopenharmony_ci goto out; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (unlikely(f6i->nh)) { 5688c2ecf20Sopenharmony_ci nh = nexthop_fib6_nh(f6i->nh); 5698c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) 5708c2ecf20Sopenharmony_ci goto out_blackhole; 5718c2ecf20Sopenharmony_ci } else { 5728c2ecf20Sopenharmony_ci nh = f6i->fib6_nh; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) { 5768c2ecf20Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 5778c2ecf20Sopenharmony_ci nh = res->f6i->fib6_nh; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ciout: 5808c2ecf20Sopenharmony_ci res->nh = nh; 5818c2ecf20Sopenharmony_ci res->fib6_type = res->f6i->fib6_type; 5828c2ecf20Sopenharmony_ci res->fib6_flags = res->f6i->fib6_flags; 5838c2ecf20Sopenharmony_ci return; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciout_blackhole: 5868c2ecf20Sopenharmony_ci res->fib6_flags |= RTF_REJECT; 5878c2ecf20Sopenharmony_ci res->fib6_type = RTN_BLACKHOLE; 5888c2ecf20Sopenharmony_ci res->nh = nh; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 5928c2ecf20Sopenharmony_cistruct __rt6_probe_work { 5938c2ecf20Sopenharmony_ci struct work_struct work; 5948c2ecf20Sopenharmony_ci struct in6_addr target; 5958c2ecf20Sopenharmony_ci struct net_device *dev; 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void rt6_probe_deferred(struct work_struct *w) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct in6_addr mcaddr; 6018c2ecf20Sopenharmony_ci struct __rt6_probe_work *work = 6028c2ecf20Sopenharmony_ci container_of(w, struct __rt6_probe_work, work); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(&work->target, &mcaddr); 6058c2ecf20Sopenharmony_ci ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); 6068c2ecf20Sopenharmony_ci dev_put(work->dev); 6078c2ecf20Sopenharmony_ci kfree(work); 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic void rt6_probe(struct fib6_nh *fib6_nh) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct __rt6_probe_work *work = NULL; 6138c2ecf20Sopenharmony_ci const struct in6_addr *nh_gw; 6148c2ecf20Sopenharmony_ci unsigned long last_probe; 6158c2ecf20Sopenharmony_ci struct neighbour *neigh; 6168c2ecf20Sopenharmony_ci struct net_device *dev; 6178c2ecf20Sopenharmony_ci struct inet6_dev *idev; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * Okay, this does not seem to be appropriate 6218c2ecf20Sopenharmony_ci * for now, however, we need to check if it 6228c2ecf20Sopenharmony_ci * is really so; aka Router Reachability Probing. 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * Router Reachability Probe MUST be rate-limited 6258c2ecf20Sopenharmony_ci * to no more than one per minute. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci if (!fib6_nh->fib_nh_gw_family) 6288c2ecf20Sopenharmony_ci return; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci nh_gw = &fib6_nh->fib_nh_gw6; 6318c2ecf20Sopenharmony_ci dev = fib6_nh->fib_nh_dev; 6328c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 6338c2ecf20Sopenharmony_ci last_probe = READ_ONCE(fib6_nh->last_probe); 6348c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 6358c2ecf20Sopenharmony_ci if (!idev) 6368c2ecf20Sopenharmony_ci goto out; 6378c2ecf20Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(dev, nh_gw); 6388c2ecf20Sopenharmony_ci if (neigh) { 6398c2ecf20Sopenharmony_ci if (neigh->nud_state & NUD_VALID) 6408c2ecf20Sopenharmony_ci goto out; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci write_lock(&neigh->lock); 6438c2ecf20Sopenharmony_ci if (!(neigh->nud_state & NUD_VALID) && 6448c2ecf20Sopenharmony_ci time_after(jiffies, 6458c2ecf20Sopenharmony_ci neigh->updated + idev->cnf.rtr_probe_interval)) { 6468c2ecf20Sopenharmony_ci work = kmalloc(sizeof(*work), GFP_ATOMIC); 6478c2ecf20Sopenharmony_ci if (work) 6488c2ecf20Sopenharmony_ci __neigh_set_probe_once(neigh); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci write_unlock(&neigh->lock); 6518c2ecf20Sopenharmony_ci } else if (time_after(jiffies, last_probe + 6528c2ecf20Sopenharmony_ci idev->cnf.rtr_probe_interval)) { 6538c2ecf20Sopenharmony_ci work = kmalloc(sizeof(*work), GFP_ATOMIC); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (!work || cmpxchg(&fib6_nh->last_probe, 6578c2ecf20Sopenharmony_ci last_probe, jiffies) != last_probe) { 6588c2ecf20Sopenharmony_ci kfree(work); 6598c2ecf20Sopenharmony_ci } else { 6608c2ecf20Sopenharmony_ci INIT_WORK(&work->work, rt6_probe_deferred); 6618c2ecf20Sopenharmony_ci work->target = *nh_gw; 6628c2ecf20Sopenharmony_ci dev_hold(dev); 6638c2ecf20Sopenharmony_ci work->dev = dev; 6648c2ecf20Sopenharmony_ci schedule_work(&work->work); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ciout: 6688c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci#else 6718c2ecf20Sopenharmony_cistatic inline void rt6_probe(struct fib6_nh *fib6_nh) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci#endif 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/* 6778c2ecf20Sopenharmony_ci * Default Router Selection (RFC 2461 6.3.6) 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_cistatic enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 6828c2ecf20Sopenharmony_ci struct neighbour *neigh; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 6858c2ecf20Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev, 6868c2ecf20Sopenharmony_ci &fib6_nh->fib_nh_gw6); 6878c2ecf20Sopenharmony_ci if (neigh) { 6888c2ecf20Sopenharmony_ci read_lock(&neigh->lock); 6898c2ecf20Sopenharmony_ci if (neigh->nud_state & NUD_VALID) 6908c2ecf20Sopenharmony_ci ret = RT6_NUD_SUCCEED; 6918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 6928c2ecf20Sopenharmony_ci else if (!(neigh->nud_state & NUD_FAILED)) 6938c2ecf20Sopenharmony_ci ret = RT6_NUD_SUCCEED; 6948c2ecf20Sopenharmony_ci else 6958c2ecf20Sopenharmony_ci ret = RT6_NUD_FAIL_PROBE; 6968c2ecf20Sopenharmony_ci#endif 6978c2ecf20Sopenharmony_ci read_unlock(&neigh->lock); 6988c2ecf20Sopenharmony_ci } else { 6998c2ecf20Sopenharmony_ci ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 7008c2ecf20Sopenharmony_ci RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return ret; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif, 7088c2ecf20Sopenharmony_ci int strict) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci int m = 0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (!oif || nh->fib_nh_dev->ifindex == oif) 7138c2ecf20Sopenharmony_ci m = 2; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (!m && (strict & RT6_LOOKUP_F_IFACE)) 7168c2ecf20Sopenharmony_ci return RT6_NUD_FAIL_HARD; 7178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 7188c2ecf20Sopenharmony_ci m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2; 7198c2ecf20Sopenharmony_ci#endif 7208c2ecf20Sopenharmony_ci if ((strict & RT6_LOOKUP_F_REACHABLE) && 7218c2ecf20Sopenharmony_ci !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) { 7228c2ecf20Sopenharmony_ci int n = rt6_check_neigh(nh); 7238c2ecf20Sopenharmony_ci if (n < 0) 7248c2ecf20Sopenharmony_ci return n; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci return m; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic bool find_match(struct fib6_nh *nh, u32 fib6_flags, 7308c2ecf20Sopenharmony_ci int oif, int strict, int *mpri, bool *do_rr) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci bool match_do_rr = false; 7338c2ecf20Sopenharmony_ci bool rc = false; 7348c2ecf20Sopenharmony_ci int m; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) 7378c2ecf20Sopenharmony_ci goto out; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (ip6_ignore_linkdown(nh->fib_nh_dev) && 7408c2ecf20Sopenharmony_ci nh->fib_nh_flags & RTNH_F_LINKDOWN && 7418c2ecf20Sopenharmony_ci !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) 7428c2ecf20Sopenharmony_ci goto out; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci m = rt6_score_route(nh, fib6_flags, oif, strict); 7458c2ecf20Sopenharmony_ci if (m == RT6_NUD_FAIL_DO_RR) { 7468c2ecf20Sopenharmony_ci match_do_rr = true; 7478c2ecf20Sopenharmony_ci m = 0; /* lowest valid score */ 7488c2ecf20Sopenharmony_ci } else if (m == RT6_NUD_FAIL_HARD) { 7498c2ecf20Sopenharmony_ci goto out; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (strict & RT6_LOOKUP_F_REACHABLE) 7538c2ecf20Sopenharmony_ci rt6_probe(nh); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 7568c2ecf20Sopenharmony_ci if (m > *mpri) { 7578c2ecf20Sopenharmony_ci *do_rr = match_do_rr; 7588c2ecf20Sopenharmony_ci *mpri = m; 7598c2ecf20Sopenharmony_ci rc = true; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ciout: 7628c2ecf20Sopenharmony_ci return rc; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistruct fib6_nh_frl_arg { 7668c2ecf20Sopenharmony_ci u32 flags; 7678c2ecf20Sopenharmony_ci int oif; 7688c2ecf20Sopenharmony_ci int strict; 7698c2ecf20Sopenharmony_ci int *mpri; 7708c2ecf20Sopenharmony_ci bool *do_rr; 7718c2ecf20Sopenharmony_ci struct fib6_nh *nh; 7728c2ecf20Sopenharmony_ci}; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic int rt6_nh_find_match(struct fib6_nh *nh, void *_arg) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct fib6_nh_frl_arg *arg = _arg; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci arg->nh = nh; 7798c2ecf20Sopenharmony_ci return find_match(nh, arg->flags, arg->oif, arg->strict, 7808c2ecf20Sopenharmony_ci arg->mpri, arg->do_rr); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic void __find_rr_leaf(struct fib6_info *f6i_start, 7848c2ecf20Sopenharmony_ci struct fib6_info *nomatch, u32 metric, 7858c2ecf20Sopenharmony_ci struct fib6_result *res, struct fib6_info **cont, 7868c2ecf20Sopenharmony_ci int oif, int strict, bool *do_rr, int *mpri) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct fib6_info *f6i; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci for (f6i = f6i_start; 7918c2ecf20Sopenharmony_ci f6i && f6i != nomatch; 7928c2ecf20Sopenharmony_ci f6i = rcu_dereference(f6i->fib6_next)) { 7938c2ecf20Sopenharmony_ci bool matched = false; 7948c2ecf20Sopenharmony_ci struct fib6_nh *nh; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (cont && f6i->fib6_metric != metric) { 7978c2ecf20Sopenharmony_ci *cont = f6i; 7988c2ecf20Sopenharmony_ci return; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (fib6_check_expired(f6i)) 8028c2ecf20Sopenharmony_ci continue; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (unlikely(f6i->nh)) { 8058c2ecf20Sopenharmony_ci struct fib6_nh_frl_arg arg = { 8068c2ecf20Sopenharmony_ci .flags = f6i->fib6_flags, 8078c2ecf20Sopenharmony_ci .oif = oif, 8088c2ecf20Sopenharmony_ci .strict = strict, 8098c2ecf20Sopenharmony_ci .mpri = mpri, 8108c2ecf20Sopenharmony_ci .do_rr = do_rr 8118c2ecf20Sopenharmony_ci }; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) { 8148c2ecf20Sopenharmony_ci res->fib6_flags = RTF_REJECT; 8158c2ecf20Sopenharmony_ci res->fib6_type = RTN_BLACKHOLE; 8168c2ecf20Sopenharmony_ci res->f6i = f6i; 8178c2ecf20Sopenharmony_ci res->nh = nexthop_fib6_nh(f6i->nh); 8188c2ecf20Sopenharmony_ci return; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match, 8218c2ecf20Sopenharmony_ci &arg)) { 8228c2ecf20Sopenharmony_ci matched = true; 8238c2ecf20Sopenharmony_ci nh = arg.nh; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } else { 8268c2ecf20Sopenharmony_ci nh = f6i->fib6_nh; 8278c2ecf20Sopenharmony_ci if (find_match(nh, f6i->fib6_flags, oif, strict, 8288c2ecf20Sopenharmony_ci mpri, do_rr)) 8298c2ecf20Sopenharmony_ci matched = true; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci if (matched) { 8328c2ecf20Sopenharmony_ci res->f6i = f6i; 8338c2ecf20Sopenharmony_ci res->nh = nh; 8348c2ecf20Sopenharmony_ci res->fib6_flags = f6i->fib6_flags; 8358c2ecf20Sopenharmony_ci res->fib6_type = f6i->fib6_type; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf, 8418c2ecf20Sopenharmony_ci struct fib6_info *rr_head, int oif, int strict, 8428c2ecf20Sopenharmony_ci bool *do_rr, struct fib6_result *res) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci u32 metric = rr_head->fib6_metric; 8458c2ecf20Sopenharmony_ci struct fib6_info *cont = NULL; 8468c2ecf20Sopenharmony_ci int mpri = -1; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci __find_rr_leaf(rr_head, NULL, metric, res, &cont, 8498c2ecf20Sopenharmony_ci oif, strict, do_rr, &mpri); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci __find_rr_leaf(leaf, rr_head, metric, res, &cont, 8528c2ecf20Sopenharmony_ci oif, strict, do_rr, &mpri); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (res->f6i || !cont) 8558c2ecf20Sopenharmony_ci return; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci __find_rr_leaf(cont, NULL, metric, res, NULL, 8588c2ecf20Sopenharmony_ci oif, strict, do_rr, &mpri); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic void rt6_select(struct net *net, struct fib6_node *fn, int oif, 8628c2ecf20Sopenharmony_ci struct fib6_result *res, int strict) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference(fn->leaf); 8658c2ecf20Sopenharmony_ci struct fib6_info *rt0; 8668c2ecf20Sopenharmony_ci bool do_rr = false; 8678c2ecf20Sopenharmony_ci int key_plen; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* make sure this function or its helpers sets f6i */ 8708c2ecf20Sopenharmony_ci res->f6i = NULL; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!leaf || leaf == net->ipv6.fib6_null_entry) 8738c2ecf20Sopenharmony_ci goto out; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci rt0 = rcu_dereference(fn->rr_ptr); 8768c2ecf20Sopenharmony_ci if (!rt0) 8778c2ecf20Sopenharmony_ci rt0 = leaf; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Double check to make sure fn is not an intermediate node 8808c2ecf20Sopenharmony_ci * and fn->leaf does not points to its child's leaf 8818c2ecf20Sopenharmony_ci * (This might happen if all routes under fn are deleted from 8828c2ecf20Sopenharmony_ci * the tree and fib6_repair_tree() is called on the node.) 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci key_plen = rt0->fib6_dst.plen; 8858c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 8868c2ecf20Sopenharmony_ci if (rt0->fib6_src.plen) 8878c2ecf20Sopenharmony_ci key_plen = rt0->fib6_src.plen; 8888c2ecf20Sopenharmony_ci#endif 8898c2ecf20Sopenharmony_ci if (fn->fn_bit != key_plen) 8908c2ecf20Sopenharmony_ci goto out; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res); 8938c2ecf20Sopenharmony_ci if (do_rr) { 8948c2ecf20Sopenharmony_ci struct fib6_info *next = rcu_dereference(rt0->fib6_next); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* no entries matched; do round-robin */ 8978c2ecf20Sopenharmony_ci if (!next || next->fib6_metric != rt0->fib6_metric) 8988c2ecf20Sopenharmony_ci next = leaf; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (next != rt0) { 9018c2ecf20Sopenharmony_ci spin_lock_bh(&leaf->fib6_table->tb6_lock); 9028c2ecf20Sopenharmony_ci /* make sure next is not being deleted from the tree */ 9038c2ecf20Sopenharmony_ci if (next->fib6_node) 9048c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->rr_ptr, next); 9058c2ecf20Sopenharmony_ci spin_unlock_bh(&leaf->fib6_table->tb6_lock); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ciout: 9108c2ecf20Sopenharmony_ci if (!res->f6i) { 9118c2ecf20Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 9128c2ecf20Sopenharmony_ci res->nh = res->f6i->fib6_nh; 9138c2ecf20Sopenharmony_ci res->fib6_flags = res->f6i->fib6_flags; 9148c2ecf20Sopenharmony_ci res->fib6_type = res->f6i->fib6_type; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic bool rt6_is_gw_or_nonexthop(const struct fib6_result *res) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci return (res->f6i->fib6_flags & RTF_NONEXTHOP) || 9218c2ecf20Sopenharmony_ci res->nh->fib_nh_gw_family; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 9258c2ecf20Sopenharmony_ciint rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 9268c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 9298c2ecf20Sopenharmony_ci struct route_info *rinfo = (struct route_info *) opt; 9308c2ecf20Sopenharmony_ci struct in6_addr prefix_buf, *prefix; 9318c2ecf20Sopenharmony_ci unsigned int pref; 9328c2ecf20Sopenharmony_ci unsigned long lifetime; 9338c2ecf20Sopenharmony_ci struct fib6_info *rt; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (len < sizeof(struct route_info)) { 9368c2ecf20Sopenharmony_ci return -EINVAL; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Sanity check for prefix_len and length */ 9408c2ecf20Sopenharmony_ci if (rinfo->length > 3) { 9418c2ecf20Sopenharmony_ci return -EINVAL; 9428c2ecf20Sopenharmony_ci } else if (rinfo->prefix_len > 128) { 9438c2ecf20Sopenharmony_ci return -EINVAL; 9448c2ecf20Sopenharmony_ci } else if (rinfo->prefix_len > 64) { 9458c2ecf20Sopenharmony_ci if (rinfo->length < 2) { 9468c2ecf20Sopenharmony_ci return -EINVAL; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci } else if (rinfo->prefix_len > 0) { 9498c2ecf20Sopenharmony_ci if (rinfo->length < 1) { 9508c2ecf20Sopenharmony_ci return -EINVAL; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci pref = rinfo->route_pref; 9558c2ecf20Sopenharmony_ci if (pref == ICMPV6_ROUTER_PREF_INVALID) 9568c2ecf20Sopenharmony_ci return -EINVAL; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (rinfo->length == 3) 9618c2ecf20Sopenharmony_ci prefix = (struct in6_addr *)rinfo->prefix; 9628c2ecf20Sopenharmony_ci else { 9638c2ecf20Sopenharmony_ci /* this function is safe */ 9648c2ecf20Sopenharmony_ci ipv6_addr_prefix(&prefix_buf, 9658c2ecf20Sopenharmony_ci (struct in6_addr *)rinfo->prefix, 9668c2ecf20Sopenharmony_ci rinfo->prefix_len); 9678c2ecf20Sopenharmony_ci prefix = &prefix_buf; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (rinfo->prefix_len == 0) 9718c2ecf20Sopenharmony_ci rt = rt6_get_dflt_router(net, gwaddr, dev); 9728c2ecf20Sopenharmony_ci else 9738c2ecf20Sopenharmony_ci rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 9748c2ecf20Sopenharmony_ci gwaddr, dev); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (rt && !lifetime) { 9778c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 9788c2ecf20Sopenharmony_ci rt = NULL; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (!rt && lifetime) 9828c2ecf20Sopenharmony_ci rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, 9838c2ecf20Sopenharmony_ci dev, pref); 9848c2ecf20Sopenharmony_ci else if (rt) 9858c2ecf20Sopenharmony_ci rt->fib6_flags = RTF_ROUTEINFO | 9868c2ecf20Sopenharmony_ci (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (rt) { 9898c2ecf20Sopenharmony_ci if (!addrconf_finite_timeout(lifetime)) 9908c2ecf20Sopenharmony_ci fib6_clean_expires(rt); 9918c2ecf20Sopenharmony_ci else 9928c2ecf20Sopenharmony_ci fib6_set_expires(rt, jiffies + HZ * lifetime); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci fib6_info_release(rt); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci return 0; 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci#endif 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/* 10018c2ecf20Sopenharmony_ci * Misc support functions 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci/* called with rcu_lock held */ 10058c2ecf20Sopenharmony_cistatic struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct net_device *dev = res->nh->fib_nh_dev; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) { 10108c2ecf20Sopenharmony_ci /* for copies of local routes, dst->dev needs to be the 10118c2ecf20Sopenharmony_ci * device if it is a master device, the master device if 10128c2ecf20Sopenharmony_ci * device is enslaved, and the loopback as the default 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci if (netif_is_l3_slave(dev) && 10158c2ecf20Sopenharmony_ci !rt6_need_strict(&res->f6i->fib6_dst.addr)) 10168c2ecf20Sopenharmony_ci dev = l3mdev_master_dev_rcu(dev); 10178c2ecf20Sopenharmony_ci else if (!netif_is_l3_master(dev)) 10188c2ecf20Sopenharmony_ci dev = dev_net(dev)->loopback_dev; 10198c2ecf20Sopenharmony_ci /* last case is netif_is_l3_master(dev) is true in which 10208c2ecf20Sopenharmony_ci * case we want dev returned to be dev 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci return dev; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic const int fib6_prop[RTN_MAX + 1] = { 10288c2ecf20Sopenharmony_ci [RTN_UNSPEC] = 0, 10298c2ecf20Sopenharmony_ci [RTN_UNICAST] = 0, 10308c2ecf20Sopenharmony_ci [RTN_LOCAL] = 0, 10318c2ecf20Sopenharmony_ci [RTN_BROADCAST] = 0, 10328c2ecf20Sopenharmony_ci [RTN_ANYCAST] = 0, 10338c2ecf20Sopenharmony_ci [RTN_MULTICAST] = 0, 10348c2ecf20Sopenharmony_ci [RTN_BLACKHOLE] = -EINVAL, 10358c2ecf20Sopenharmony_ci [RTN_UNREACHABLE] = -EHOSTUNREACH, 10368c2ecf20Sopenharmony_ci [RTN_PROHIBIT] = -EACCES, 10378c2ecf20Sopenharmony_ci [RTN_THROW] = -EAGAIN, 10388c2ecf20Sopenharmony_ci [RTN_NAT] = -EINVAL, 10398c2ecf20Sopenharmony_ci [RTN_XRESOLVE] = -EINVAL, 10408c2ecf20Sopenharmony_ci}; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci return fib6_prop[fib6_type]; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic unsigned short fib6_info_dst_flags(struct fib6_info *rt) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci unsigned short flags = 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (rt->dst_nocount) 10528c2ecf20Sopenharmony_ci flags |= DST_NOCOUNT; 10538c2ecf20Sopenharmony_ci if (rt->dst_nopolicy) 10548c2ecf20Sopenharmony_ci flags |= DST_NOPOLICY; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return flags; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci rt->dst.error = ip6_rt_type_to_error(fib6_type); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci switch (fib6_type) { 10648c2ecf20Sopenharmony_ci case RTN_BLACKHOLE: 10658c2ecf20Sopenharmony_ci rt->dst.output = dst_discard_out; 10668c2ecf20Sopenharmony_ci rt->dst.input = dst_discard; 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci case RTN_PROHIBIT: 10698c2ecf20Sopenharmony_ci rt->dst.output = ip6_pkt_prohibit_out; 10708c2ecf20Sopenharmony_ci rt->dst.input = ip6_pkt_prohibit; 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci case RTN_THROW: 10738c2ecf20Sopenharmony_ci case RTN_UNREACHABLE: 10748c2ecf20Sopenharmony_ci default: 10758c2ecf20Sopenharmony_ci rt->dst.output = ip6_pkt_discard_out; 10768c2ecf20Sopenharmony_ci rt->dst.input = ip6_pkt_discard; 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (res->fib6_flags & RTF_REJECT) { 10868c2ecf20Sopenharmony_ci ip6_rt_init_dst_reject(rt, res->fib6_type); 10878c2ecf20Sopenharmony_ci return; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci rt->dst.error = 0; 10918c2ecf20Sopenharmony_ci rt->dst.output = ip6_output; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) { 10948c2ecf20Sopenharmony_ci rt->dst.input = ip6_input; 10958c2ecf20Sopenharmony_ci } else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) { 10968c2ecf20Sopenharmony_ci rt->dst.input = ip6_mc_input; 10978c2ecf20Sopenharmony_ci } else { 10988c2ecf20Sopenharmony_ci rt->dst.input = ip6_forward; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (res->nh->fib_nh_lws) { 11028c2ecf20Sopenharmony_ci rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws); 11038c2ecf20Sopenharmony_ci lwtunnel_set_redirect(&rt->dst); 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci rt->dst.lastuse = jiffies; 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/* Caller must already hold reference to @from */ 11108c2ecf20Sopenharmony_cistatic void rt6_set_from(struct rt6_info *rt, struct fib6_info *from) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci rt->rt6i_flags &= ~RTF_EXPIRES; 11138c2ecf20Sopenharmony_ci rcu_assign_pointer(rt->from, from); 11148c2ecf20Sopenharmony_ci ip_dst_init_metrics(&rt->dst, from->fib6_metrics); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci/* Caller must already hold reference to f6i in result */ 11188c2ecf20Sopenharmony_cistatic void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci const struct fib6_nh *nh = res->nh; 11218c2ecf20Sopenharmony_ci const struct net_device *dev = nh->fib_nh_dev; 11228c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ip6_rt_init_dst(rt, res); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci rt->rt6i_dst = f6i->fib6_dst; 11278c2ecf20Sopenharmony_ci rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL; 11288c2ecf20Sopenharmony_ci rt->rt6i_flags = res->fib6_flags; 11298c2ecf20Sopenharmony_ci if (nh->fib_nh_gw_family) { 11308c2ecf20Sopenharmony_ci rt->rt6i_gateway = nh->fib_nh_gw6; 11318c2ecf20Sopenharmony_ci rt->rt6i_flags |= RTF_GATEWAY; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci rt6_set_from(rt, f6i); 11348c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 11358c2ecf20Sopenharmony_ci rt->rt6i_src = f6i->fib6_src; 11368c2ecf20Sopenharmony_ci#endif 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic struct fib6_node* fib6_backtrack(struct fib6_node *fn, 11408c2ecf20Sopenharmony_ci struct in6_addr *saddr) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct fib6_node *pn, *sn; 11438c2ecf20Sopenharmony_ci while (1) { 11448c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_TL_ROOT) 11458c2ecf20Sopenharmony_ci return NULL; 11468c2ecf20Sopenharmony_ci pn = rcu_dereference(fn->parent); 11478c2ecf20Sopenharmony_ci sn = FIB6_SUBTREE(pn); 11488c2ecf20Sopenharmony_ci if (sn && sn != fn) 11498c2ecf20Sopenharmony_ci fn = fib6_node_lookup(sn, NULL, saddr); 11508c2ecf20Sopenharmony_ci else 11518c2ecf20Sopenharmony_ci fn = pn; 11528c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_RTINFO) 11538c2ecf20Sopenharmony_ci return fn; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic bool ip6_hold_safe(struct net *net, struct rt6_info **prt) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct rt6_info *rt = *prt; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (dst_hold_safe(&rt->dst)) 11628c2ecf20Sopenharmony_ci return true; 11638c2ecf20Sopenharmony_ci if (net) { 11648c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 11658c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 11668c2ecf20Sopenharmony_ci } else { 11678c2ecf20Sopenharmony_ci rt = NULL; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci *prt = rt; 11708c2ecf20Sopenharmony_ci return false; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci/* called with rcu_lock held */ 11748c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct net_device *dev = res->nh->fib_nh_dev; 11778c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 11788c2ecf20Sopenharmony_ci unsigned short flags; 11798c2ecf20Sopenharmony_ci struct rt6_info *nrt; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 11828c2ecf20Sopenharmony_ci goto fallback; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci flags = fib6_info_dst_flags(f6i); 11858c2ecf20Sopenharmony_ci nrt = ip6_dst_alloc(dev_net(dev), dev, flags); 11868c2ecf20Sopenharmony_ci if (!nrt) { 11878c2ecf20Sopenharmony_ci fib6_info_release(f6i); 11888c2ecf20Sopenharmony_ci goto fallback; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci ip6_rt_copy_init(nrt, res); 11928c2ecf20Sopenharmony_ci return nrt; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cifallback: 11958c2ecf20Sopenharmony_ci nrt = dev_net(dev)->ipv6.ip6_null_entry; 11968c2ecf20Sopenharmony_ci dst_hold(&nrt->dst); 11978c2ecf20Sopenharmony_ci return nrt; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net, 12018c2ecf20Sopenharmony_ci struct fib6_table *table, 12028c2ecf20Sopenharmony_ci struct flowi6 *fl6, 12038c2ecf20Sopenharmony_ci const struct sk_buff *skb, 12048c2ecf20Sopenharmony_ci int flags) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct fib6_result res = {}; 12078c2ecf20Sopenharmony_ci struct fib6_node *fn; 12088c2ecf20Sopenharmony_ci struct rt6_info *rt; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 12118c2ecf20Sopenharmony_ci flags &= ~RT6_LOOKUP_F_IFACE; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci rcu_read_lock(); 12148c2ecf20Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 12158c2ecf20Sopenharmony_cirestart: 12168c2ecf20Sopenharmony_ci res.f6i = rcu_dereference(fn->leaf); 12178c2ecf20Sopenharmony_ci if (!res.f6i) 12188c2ecf20Sopenharmony_ci res.f6i = net->ipv6.fib6_null_entry; 12198c2ecf20Sopenharmony_ci else 12208c2ecf20Sopenharmony_ci rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif, 12218c2ecf20Sopenharmony_ci flags); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (res.f6i == net->ipv6.fib6_null_entry) { 12248c2ecf20Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 12258c2ecf20Sopenharmony_ci if (fn) 12268c2ecf20Sopenharmony_ci goto restart; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 12298c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 12308c2ecf20Sopenharmony_ci goto out; 12318c2ecf20Sopenharmony_ci } else if (res.fib6_flags & RTF_REJECT) { 12328c2ecf20Sopenharmony_ci goto do_create; 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci fib6_select_path(net, &res, fl6, fl6->flowi6_oif, 12368c2ecf20Sopenharmony_ci fl6->flowi6_oif != 0, skb, flags); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Search through exception table */ 12398c2ecf20Sopenharmony_ci rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); 12408c2ecf20Sopenharmony_ci if (rt) { 12418c2ecf20Sopenharmony_ci if (ip6_hold_safe(net, &rt)) 12428c2ecf20Sopenharmony_ci dst_use_noref(&rt->dst, jiffies); 12438c2ecf20Sopenharmony_ci } else { 12448c2ecf20Sopenharmony_cido_create: 12458c2ecf20Sopenharmony_ci rt = ip6_create_rt_rcu(&res); 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ciout: 12498c2ecf20Sopenharmony_ci trace_fib6_table_lookup(net, &res, table, fl6); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci rcu_read_unlock(); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci return rt; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 12578c2ecf20Sopenharmony_ci const struct sk_buff *skb, int flags) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup); 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_lookup); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistruct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 12648c2ecf20Sopenharmony_ci const struct in6_addr *saddr, int oif, 12658c2ecf20Sopenharmony_ci const struct sk_buff *skb, int strict) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 12688c2ecf20Sopenharmony_ci .flowi6_oif = oif, 12698c2ecf20Sopenharmony_ci .daddr = *daddr, 12708c2ecf20Sopenharmony_ci }; 12718c2ecf20Sopenharmony_ci struct dst_entry *dst; 12728c2ecf20Sopenharmony_ci int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (saddr) { 12758c2ecf20Sopenharmony_ci memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 12768c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup); 12808c2ecf20Sopenharmony_ci if (dst->error == 0) 12818c2ecf20Sopenharmony_ci return (struct rt6_info *) dst; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci dst_release(dst); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci return NULL; 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rt6_lookup); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci/* ip6_ins_rt is called with FREE table->tb6_lock. 12908c2ecf20Sopenharmony_ci * It takes new route entry, the addition fails by any reason the 12918c2ecf20Sopenharmony_ci * route is released. 12928c2ecf20Sopenharmony_ci * Caller must hold dst before calling it. 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cistatic int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info, 12968c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci int err; 12998c2ecf20Sopenharmony_ci struct fib6_table *table; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci table = rt->fib6_table; 13028c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 13038c2ecf20Sopenharmony_ci err = fib6_add(&table->tb6_root, rt, info, extack); 13048c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci return err; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ciint ip6_ins_rt(struct net *net, struct fib6_info *rt) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct nl_info info = { .nl_net = net, }; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci return __ip6_ins_rt(rt, &info, NULL); 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res, 13178c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 13188c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 13218c2ecf20Sopenharmony_ci struct net_device *dev; 13228c2ecf20Sopenharmony_ci struct rt6_info *rt; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * Clone the route. 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 13298c2ecf20Sopenharmony_ci return NULL; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci dev = ip6_rt_get_dev_rcu(res); 13328c2ecf20Sopenharmony_ci rt = ip6_dst_alloc(dev_net(dev), dev, 0); 13338c2ecf20Sopenharmony_ci if (!rt) { 13348c2ecf20Sopenharmony_ci fib6_info_release(f6i); 13358c2ecf20Sopenharmony_ci return NULL; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci ip6_rt_copy_init(rt, res); 13398c2ecf20Sopenharmony_ci rt->rt6i_flags |= RTF_CACHE; 13408c2ecf20Sopenharmony_ci rt->rt6i_dst.addr = *daddr; 13418c2ecf20Sopenharmony_ci rt->rt6i_dst.plen = 128; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (!rt6_is_gw_or_nonexthop(res)) { 13448c2ecf20Sopenharmony_ci if (f6i->fib6_dst.plen != 128 && 13458c2ecf20Sopenharmony_ci ipv6_addr_equal(&f6i->fib6_dst.addr, daddr)) 13468c2ecf20Sopenharmony_ci rt->rt6i_flags |= RTF_ANYCAST; 13478c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 13488c2ecf20Sopenharmony_ci if (rt->rt6i_src.plen && saddr) { 13498c2ecf20Sopenharmony_ci rt->rt6i_src.addr = *saddr; 13508c2ecf20Sopenharmony_ci rt->rt6i_src.plen = 128; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci#endif 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return rt; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 13618c2ecf20Sopenharmony_ci unsigned short flags = fib6_info_dst_flags(f6i); 13628c2ecf20Sopenharmony_ci struct net_device *dev; 13638c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 13668c2ecf20Sopenharmony_ci return NULL; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci rcu_read_lock(); 13698c2ecf20Sopenharmony_ci dev = ip6_rt_get_dev_rcu(res); 13708c2ecf20Sopenharmony_ci pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT); 13718c2ecf20Sopenharmony_ci rcu_read_unlock(); 13728c2ecf20Sopenharmony_ci if (!pcpu_rt) { 13738c2ecf20Sopenharmony_ci fib6_info_release(f6i); 13748c2ecf20Sopenharmony_ci return NULL; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci ip6_rt_copy_init(pcpu_rt, res); 13778c2ecf20Sopenharmony_ci pcpu_rt->rt6i_flags |= RTF_PCPU; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (f6i->nh) 13808c2ecf20Sopenharmony_ci pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev)); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci return pcpu_rt; 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic bool rt6_is_valid(const struct rt6_info *rt6) 13868c2ecf20Sopenharmony_ci{ 13878c2ecf20Sopenharmony_ci return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev)); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci/* It should be called with rcu_read_lock() acquired */ 13918c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) { 13988c2ecf20Sopenharmony_ci struct rt6_info *prev, **p; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci p = this_cpu_ptr(res->nh->rt6i_pcpu); 14018c2ecf20Sopenharmony_ci /* Paired with READ_ONCE() in __fib6_drop_pcpu_from() */ 14028c2ecf20Sopenharmony_ci prev = xchg(p, NULL); 14038c2ecf20Sopenharmony_ci if (prev) { 14048c2ecf20Sopenharmony_ci dst_dev_put(&prev->dst); 14058c2ecf20Sopenharmony_ci dst_release(&prev->dst); 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci pcpu_rt = NULL; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return pcpu_rt; 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_make_pcpu_route(struct net *net, 14158c2ecf20Sopenharmony_ci const struct fib6_result *res) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt, *prev, **p; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci pcpu_rt = ip6_rt_pcpu_alloc(res); 14208c2ecf20Sopenharmony_ci if (!pcpu_rt) 14218c2ecf20Sopenharmony_ci return NULL; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci p = this_cpu_ptr(res->nh->rt6i_pcpu); 14248c2ecf20Sopenharmony_ci prev = cmpxchg(p, NULL, pcpu_rt); 14258c2ecf20Sopenharmony_ci BUG_ON(prev); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (res->f6i->fib6_destroying) { 14288c2ecf20Sopenharmony_ci struct fib6_info *from; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL); 14318c2ecf20Sopenharmony_ci fib6_info_release(from); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci return pcpu_rt; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci/* exception hash table implementation 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(rt6_exception_lock); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci/* Remove rt6_ex from hash table and free the memory 14428c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock 14438c2ecf20Sopenharmony_ci */ 14448c2ecf20Sopenharmony_cistatic void rt6_remove_exception(struct rt6_exception_bucket *bucket, 14458c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci struct fib6_info *from; 14488c2ecf20Sopenharmony_ci struct net *net; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (!bucket || !rt6_ex) 14518c2ecf20Sopenharmony_ci return; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci net = dev_net(rt6_ex->rt6i->dst.dev); 14548c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache--; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* purge completely the exception to allow releasing the held resources: 14578c2ecf20Sopenharmony_ci * some [sk] cache may keep the dst around for unlimited time 14588c2ecf20Sopenharmony_ci */ 14598c2ecf20Sopenharmony_ci from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL); 14608c2ecf20Sopenharmony_ci fib6_info_release(from); 14618c2ecf20Sopenharmony_ci dst_dev_put(&rt6_ex->rt6i->dst); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci hlist_del_rcu(&rt6_ex->hlist); 14648c2ecf20Sopenharmony_ci dst_release(&rt6_ex->rt6i->dst); 14658c2ecf20Sopenharmony_ci kfree_rcu(rt6_ex, rcu); 14668c2ecf20Sopenharmony_ci WARN_ON_ONCE(!bucket->depth); 14678c2ecf20Sopenharmony_ci bucket->depth--; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci/* Remove oldest rt6_ex in bucket and free the memory 14718c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock 14728c2ecf20Sopenharmony_ci */ 14738c2ecf20Sopenharmony_cistatic void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex, *oldest = NULL; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (!bucket) 14788c2ecf20Sopenharmony_ci return; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 14818c2ecf20Sopenharmony_ci if (!oldest || time_before(rt6_ex->stamp, oldest->stamp)) 14828c2ecf20Sopenharmony_ci oldest = rt6_ex; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, oldest); 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic u32 rt6_exception_hash(const struct in6_addr *dst, 14888c2ecf20Sopenharmony_ci const struct in6_addr *src) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci static siphash_key_t rt6_exception_key __read_mostly; 14918c2ecf20Sopenharmony_ci struct { 14928c2ecf20Sopenharmony_ci struct in6_addr dst; 14938c2ecf20Sopenharmony_ci struct in6_addr src; 14948c2ecf20Sopenharmony_ci } __aligned(SIPHASH_ALIGNMENT) combined = { 14958c2ecf20Sopenharmony_ci .dst = *dst, 14968c2ecf20Sopenharmony_ci }; 14978c2ecf20Sopenharmony_ci u64 val; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key)); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 15028c2ecf20Sopenharmony_ci if (src) 15038c2ecf20Sopenharmony_ci combined.src = *src; 15048c2ecf20Sopenharmony_ci#endif 15058c2ecf20Sopenharmony_ci val = siphash(&combined, sizeof(combined), &rt6_exception_key); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci/* Helper function to find the cached rt in the hash table 15118c2ecf20Sopenharmony_ci * and update bucket pointer to point to the bucket for this 15128c2ecf20Sopenharmony_ci * (daddr, saddr) pair 15138c2ecf20Sopenharmony_ci * Caller must hold rt6_exception_lock 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_cistatic struct rt6_exception * 15168c2ecf20Sopenharmony_ci__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket, 15178c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 15188c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 15218c2ecf20Sopenharmony_ci u32 hval; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (!(*bucket) || !daddr) 15248c2ecf20Sopenharmony_ci return NULL; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci hval = rt6_exception_hash(daddr, saddr); 15278c2ecf20Sopenharmony_ci *bucket += hval; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) { 15308c2ecf20Sopenharmony_ci struct rt6_info *rt6 = rt6_ex->rt6i; 15318c2ecf20Sopenharmony_ci bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 15348c2ecf20Sopenharmony_ci if (matched && saddr) 15358c2ecf20Sopenharmony_ci matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 15368c2ecf20Sopenharmony_ci#endif 15378c2ecf20Sopenharmony_ci if (matched) 15388c2ecf20Sopenharmony_ci return rt6_ex; 15398c2ecf20Sopenharmony_ci } 15408c2ecf20Sopenharmony_ci return NULL; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci/* Helper function to find the cached rt in the hash table 15448c2ecf20Sopenharmony_ci * and update bucket pointer to point to the bucket for this 15458c2ecf20Sopenharmony_ci * (daddr, saddr) pair 15468c2ecf20Sopenharmony_ci * Caller must hold rcu_read_lock() 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_cistatic struct rt6_exception * 15498c2ecf20Sopenharmony_ci__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, 15508c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 15518c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 15548c2ecf20Sopenharmony_ci u32 hval; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci if (!(*bucket) || !daddr) 15598c2ecf20Sopenharmony_ci return NULL; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci hval = rt6_exception_hash(daddr, saddr); 15628c2ecf20Sopenharmony_ci *bucket += hval; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) { 15658c2ecf20Sopenharmony_ci struct rt6_info *rt6 = rt6_ex->rt6i; 15668c2ecf20Sopenharmony_ci bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 15698c2ecf20Sopenharmony_ci if (matched && saddr) 15708c2ecf20Sopenharmony_ci matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 15718c2ecf20Sopenharmony_ci#endif 15728c2ecf20Sopenharmony_ci if (matched) 15738c2ecf20Sopenharmony_ci return rt6_ex; 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci return NULL; 15768c2ecf20Sopenharmony_ci} 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_cistatic unsigned int fib6_mtu(const struct fib6_result *res) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci const struct fib6_nh *nh = res->nh; 15818c2ecf20Sopenharmony_ci unsigned int mtu; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (res->f6i->fib6_pmtu) { 15848c2ecf20Sopenharmony_ci mtu = res->f6i->fib6_pmtu; 15858c2ecf20Sopenharmony_ci } else { 15868c2ecf20Sopenharmony_ci struct net_device *dev = nh->fib_nh_dev; 15878c2ecf20Sopenharmony_ci struct inet6_dev *idev; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci rcu_read_lock(); 15908c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 15918c2ecf20Sopenharmony_ci mtu = idev->cnf.mtu6; 15928c2ecf20Sopenharmony_ci rcu_read_unlock(); 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu); 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci#define FIB6_EXCEPTION_BUCKET_FLUSHED 0x1UL 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci/* used when the flushed bit is not relevant, only access to the bucket 16038c2ecf20Sopenharmony_ci * (ie., all bucket users except rt6_insert_exception); 16048c2ecf20Sopenharmony_ci * 16058c2ecf20Sopenharmony_ci * called under rcu lock; sometimes called with rt6_exception_lock held 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_cistatic 16088c2ecf20Sopenharmony_cistruct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh, 16098c2ecf20Sopenharmony_ci spinlock_t *lock) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (lock) 16148c2ecf20Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 16158c2ecf20Sopenharmony_ci lockdep_is_held(lock)); 16168c2ecf20Sopenharmony_ci else 16178c2ecf20Sopenharmony_ci bucket = rcu_dereference(nh->rt6i_exception_bucket); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* remove bucket flushed bit if set */ 16208c2ecf20Sopenharmony_ci if (bucket) { 16218c2ecf20Sopenharmony_ci unsigned long p = (unsigned long)bucket; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED; 16248c2ecf20Sopenharmony_ci bucket = (struct rt6_exception_bucket *)p; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci return bucket; 16288c2ecf20Sopenharmony_ci} 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_cistatic bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci unsigned long p = (unsigned long)bucket; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED); 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci/* called with rt6_exception_lock held */ 16388c2ecf20Sopenharmony_cistatic void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh, 16398c2ecf20Sopenharmony_ci spinlock_t *lock) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 16428c2ecf20Sopenharmony_ci unsigned long p; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 16458c2ecf20Sopenharmony_ci lockdep_is_held(lock)); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci p = (unsigned long)bucket; 16488c2ecf20Sopenharmony_ci p |= FIB6_EXCEPTION_BUCKET_FLUSHED; 16498c2ecf20Sopenharmony_ci bucket = (struct rt6_exception_bucket *)p; 16508c2ecf20Sopenharmony_ci rcu_assign_pointer(nh->rt6i_exception_bucket, bucket); 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic int rt6_insert_exception(struct rt6_info *nrt, 16548c2ecf20Sopenharmony_ci const struct fib6_result *res) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct net *net = dev_net(nrt->dst.dev); 16578c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 16588c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 16598c2ecf20Sopenharmony_ci struct in6_addr *src_key = NULL; 16608c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 16618c2ecf20Sopenharmony_ci struct fib6_nh *nh = res->nh; 16628c2ecf20Sopenharmony_ci int max_depth; 16638c2ecf20Sopenharmony_ci int err = 0; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 16688c2ecf20Sopenharmony_ci lockdep_is_held(&rt6_exception_lock)); 16698c2ecf20Sopenharmony_ci if (!bucket) { 16708c2ecf20Sopenharmony_ci bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket), 16718c2ecf20Sopenharmony_ci GFP_ATOMIC); 16728c2ecf20Sopenharmony_ci if (!bucket) { 16738c2ecf20Sopenharmony_ci err = -ENOMEM; 16748c2ecf20Sopenharmony_ci goto out; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci rcu_assign_pointer(nh->rt6i_exception_bucket, bucket); 16778c2ecf20Sopenharmony_ci } else if (fib6_nh_excptn_bucket_flushed(bucket)) { 16788c2ecf20Sopenharmony_ci err = -EINVAL; 16798c2ecf20Sopenharmony_ci goto out; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 16838c2ecf20Sopenharmony_ci /* fib6_src.plen != 0 indicates f6i is in subtree 16848c2ecf20Sopenharmony_ci * and exception table is indexed by a hash of 16858c2ecf20Sopenharmony_ci * both fib6_dst and fib6_src. 16868c2ecf20Sopenharmony_ci * Otherwise, the exception table is indexed by 16878c2ecf20Sopenharmony_ci * a hash of only fib6_dst. 16888c2ecf20Sopenharmony_ci */ 16898c2ecf20Sopenharmony_ci if (f6i->fib6_src.plen) 16908c2ecf20Sopenharmony_ci src_key = &nrt->rt6i_src.addr; 16918c2ecf20Sopenharmony_ci#endif 16928c2ecf20Sopenharmony_ci /* rt6_mtu_change() might lower mtu on f6i. 16938c2ecf20Sopenharmony_ci * Only insert this exception route if its mtu 16948c2ecf20Sopenharmony_ci * is less than f6i's mtu value. 16958c2ecf20Sopenharmony_ci */ 16968c2ecf20Sopenharmony_ci if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) { 16978c2ecf20Sopenharmony_ci err = -EINVAL; 16988c2ecf20Sopenharmony_ci goto out; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr, 17028c2ecf20Sopenharmony_ci src_key); 17038c2ecf20Sopenharmony_ci if (rt6_ex) 17048c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC); 17078c2ecf20Sopenharmony_ci if (!rt6_ex) { 17088c2ecf20Sopenharmony_ci err = -ENOMEM; 17098c2ecf20Sopenharmony_ci goto out; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci rt6_ex->rt6i = nrt; 17128c2ecf20Sopenharmony_ci rt6_ex->stamp = jiffies; 17138c2ecf20Sopenharmony_ci hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); 17148c2ecf20Sopenharmony_ci bucket->depth++; 17158c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache++; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci /* Randomize max depth to avoid some side channels attacks. */ 17188c2ecf20Sopenharmony_ci max_depth = FIB6_MAX_DEPTH + prandom_u32_max(FIB6_MAX_DEPTH); 17198c2ecf20Sopenharmony_ci while (bucket->depth > max_depth) 17208c2ecf20Sopenharmony_ci rt6_exception_remove_oldest(bucket); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ciout: 17238c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci /* Update fn->fn_sernum to invalidate all cached dst */ 17268c2ecf20Sopenharmony_ci if (!err) { 17278c2ecf20Sopenharmony_ci spin_lock_bh(&f6i->fib6_table->tb6_lock); 17288c2ecf20Sopenharmony_ci fib6_update_sernum(net, f6i); 17298c2ecf20Sopenharmony_ci spin_unlock_bh(&f6i->fib6_table->tb6_lock); 17308c2ecf20Sopenharmony_ci fib6_force_start_gc(net); 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci return err; 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_cistatic void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 17398c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 17408c2ecf20Sopenharmony_ci struct hlist_node *tmp; 17418c2ecf20Sopenharmony_ci int i; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 17468c2ecf20Sopenharmony_ci if (!bucket) 17478c2ecf20Sopenharmony_ci goto out; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* Prevent rt6_insert_exception() to recreate the bucket list */ 17508c2ecf20Sopenharmony_ci if (!from) 17518c2ecf20Sopenharmony_ci fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 17548c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) { 17558c2ecf20Sopenharmony_ci if (!from || 17568c2ecf20Sopenharmony_ci rcu_access_pointer(rt6_ex->rt6i->from) == from) 17578c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci WARN_ON_ONCE(!from && bucket->depth); 17608c2ecf20Sopenharmony_ci bucket++; 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ciout: 17638c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct fib6_info *f6i = arg; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci fib6_nh_flush_exceptions(nh, f6i); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci return 0; 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_civoid rt6_flush_exceptions(struct fib6_info *f6i) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci if (f6i->nh) 17788c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions, 17798c2ecf20Sopenharmony_ci f6i); 17808c2ecf20Sopenharmony_ci else 17818c2ecf20Sopenharmony_ci fib6_nh_flush_exceptions(f6i->fib6_nh, f6i); 17828c2ecf20Sopenharmony_ci} 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci/* Find cached rt in the hash table inside passed in rt 17858c2ecf20Sopenharmony_ci * Caller has to hold rcu_read_lock() 17868c2ecf20Sopenharmony_ci */ 17878c2ecf20Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 17888c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 17898c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci const struct in6_addr *src_key = NULL; 17928c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 17938c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 17948c2ecf20Sopenharmony_ci struct rt6_info *ret = NULL; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 17978c2ecf20Sopenharmony_ci /* fib6i_src.plen != 0 indicates f6i is in subtree 17988c2ecf20Sopenharmony_ci * and exception table is indexed by a hash of 17998c2ecf20Sopenharmony_ci * both fib6_dst and fib6_src. 18008c2ecf20Sopenharmony_ci * However, the src addr used to create the hash 18018c2ecf20Sopenharmony_ci * might not be exactly the passed in saddr which 18028c2ecf20Sopenharmony_ci * is a /128 addr from the flow. 18038c2ecf20Sopenharmony_ci * So we need to use f6i->fib6_src to redo lookup 18048c2ecf20Sopenharmony_ci * if the passed in saddr does not find anything. 18058c2ecf20Sopenharmony_ci * (See the logic in ip6_rt_cache_alloc() on how 18068c2ecf20Sopenharmony_ci * rt->rt6i_src is updated.) 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_ci if (res->f6i->fib6_src.plen) 18098c2ecf20Sopenharmony_ci src_key = saddr; 18108c2ecf20Sopenharmony_cifind_ex: 18118c2ecf20Sopenharmony_ci#endif 18128c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(res->nh, NULL); 18138c2ecf20Sopenharmony_ci rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) 18168c2ecf20Sopenharmony_ci ret = rt6_ex->rt6i; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 18198c2ecf20Sopenharmony_ci /* Use fib6_src as src_key and redo lookup */ 18208c2ecf20Sopenharmony_ci if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) { 18218c2ecf20Sopenharmony_ci src_key = &res->f6i->fib6_src.addr; 18228c2ecf20Sopenharmony_ci goto find_ex; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci#endif 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci return ret; 18278c2ecf20Sopenharmony_ci} 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci/* Remove the passed in cached rt from the hash table that contains it */ 18308c2ecf20Sopenharmony_cistatic int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen, 18318c2ecf20Sopenharmony_ci const struct rt6_info *rt) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci const struct in6_addr *src_key = NULL; 18348c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 18358c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 18368c2ecf20Sopenharmony_ci int err; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 18398c2ecf20Sopenharmony_ci return -ENOENT; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 18428c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 18458c2ecf20Sopenharmony_ci /* rt6i_src.plen != 0 indicates 'from' is in subtree 18468c2ecf20Sopenharmony_ci * and exception table is indexed by a hash of 18478c2ecf20Sopenharmony_ci * both rt6i_dst and rt6i_src. 18488c2ecf20Sopenharmony_ci * Otherwise, the exception table is indexed by 18498c2ecf20Sopenharmony_ci * a hash of only rt6i_dst. 18508c2ecf20Sopenharmony_ci */ 18518c2ecf20Sopenharmony_ci if (plen) 18528c2ecf20Sopenharmony_ci src_key = &rt->rt6i_src.addr; 18538c2ecf20Sopenharmony_ci#endif 18548c2ecf20Sopenharmony_ci rt6_ex = __rt6_find_exception_spinlock(&bucket, 18558c2ecf20Sopenharmony_ci &rt->rt6i_dst.addr, 18568c2ecf20Sopenharmony_ci src_key); 18578c2ecf20Sopenharmony_ci if (rt6_ex) { 18588c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 18598c2ecf20Sopenharmony_ci err = 0; 18608c2ecf20Sopenharmony_ci } else { 18618c2ecf20Sopenharmony_ci err = -ENOENT; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 18658c2ecf20Sopenharmony_ci return err; 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistruct fib6_nh_excptn_arg { 18698c2ecf20Sopenharmony_ci struct rt6_info *rt; 18708c2ecf20Sopenharmony_ci int plen; 18718c2ecf20Sopenharmony_ci}; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_cistatic int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci struct fib6_nh_excptn_arg *arg = _arg; 18768c2ecf20Sopenharmony_ci int err; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci err = fib6_nh_remove_exception(nh, arg->plen, arg->rt); 18798c2ecf20Sopenharmony_ci if (err == 0) 18808c2ecf20Sopenharmony_ci return 1; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci return 0; 18838c2ecf20Sopenharmony_ci} 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_cistatic int rt6_remove_exception_rt(struct rt6_info *rt) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct fib6_info *from; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 18908c2ecf20Sopenharmony_ci if (!from || !(rt->rt6i_flags & RTF_CACHE)) 18918c2ecf20Sopenharmony_ci return -EINVAL; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (from->nh) { 18948c2ecf20Sopenharmony_ci struct fib6_nh_excptn_arg arg = { 18958c2ecf20Sopenharmony_ci .rt = rt, 18968c2ecf20Sopenharmony_ci .plen = from->fib6_src.plen 18978c2ecf20Sopenharmony_ci }; 18988c2ecf20Sopenharmony_ci int rc; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci /* rc = 1 means an entry was found */ 19018c2ecf20Sopenharmony_ci rc = nexthop_for_each_fib6_nh(from->nh, 19028c2ecf20Sopenharmony_ci rt6_nh_remove_exception_rt, 19038c2ecf20Sopenharmony_ci &arg); 19048c2ecf20Sopenharmony_ci return rc ? 0 : -ENOENT; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci return fib6_nh_remove_exception(from->fib6_nh, 19088c2ecf20Sopenharmony_ci from->fib6_src.plen, rt); 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci/* Find rt6_ex which contains the passed in rt cache and 19128c2ecf20Sopenharmony_ci * refresh its stamp 19138c2ecf20Sopenharmony_ci */ 19148c2ecf20Sopenharmony_cistatic void fib6_nh_update_exception(const struct fib6_nh *nh, int plen, 19158c2ecf20Sopenharmony_ci const struct rt6_info *rt) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci const struct in6_addr *src_key = NULL; 19188c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 19198c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, NULL); 19228c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 19238c2ecf20Sopenharmony_ci /* rt6i_src.plen != 0 indicates 'from' is in subtree 19248c2ecf20Sopenharmony_ci * and exception table is indexed by a hash of 19258c2ecf20Sopenharmony_ci * both rt6i_dst and rt6i_src. 19268c2ecf20Sopenharmony_ci * Otherwise, the exception table is indexed by 19278c2ecf20Sopenharmony_ci * a hash of only rt6i_dst. 19288c2ecf20Sopenharmony_ci */ 19298c2ecf20Sopenharmony_ci if (plen) 19308c2ecf20Sopenharmony_ci src_key = &rt->rt6i_src.addr; 19318c2ecf20Sopenharmony_ci#endif 19328c2ecf20Sopenharmony_ci rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key); 19338c2ecf20Sopenharmony_ci if (rt6_ex) 19348c2ecf20Sopenharmony_ci rt6_ex->stamp = jiffies; 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistruct fib6_nh_match_arg { 19388c2ecf20Sopenharmony_ci const struct net_device *dev; 19398c2ecf20Sopenharmony_ci const struct in6_addr *gw; 19408c2ecf20Sopenharmony_ci struct fib6_nh *match; 19418c2ecf20Sopenharmony_ci}; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci/* determine if fib6_nh has given device and gateway */ 19448c2ecf20Sopenharmony_cistatic int fib6_nh_find_match(struct fib6_nh *nh, void *_arg) 19458c2ecf20Sopenharmony_ci{ 19468c2ecf20Sopenharmony_ci struct fib6_nh_match_arg *arg = _arg; 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci if (arg->dev != nh->fib_nh_dev || 19498c2ecf20Sopenharmony_ci (arg->gw && !nh->fib_nh_gw_family) || 19508c2ecf20Sopenharmony_ci (!arg->gw && nh->fib_nh_gw_family) || 19518c2ecf20Sopenharmony_ci (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6))) 19528c2ecf20Sopenharmony_ci return 0; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci arg->match = nh; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci /* found a match, break the loop */ 19578c2ecf20Sopenharmony_ci return 1; 19588c2ecf20Sopenharmony_ci} 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_cistatic void rt6_update_exception_stamp_rt(struct rt6_info *rt) 19618c2ecf20Sopenharmony_ci{ 19628c2ecf20Sopenharmony_ci struct fib6_info *from; 19638c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci rcu_read_lock(); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 19688c2ecf20Sopenharmony_ci if (!from || !(rt->rt6i_flags & RTF_CACHE)) 19698c2ecf20Sopenharmony_ci goto unlock; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (from->nh) { 19728c2ecf20Sopenharmony_ci struct fib6_nh_match_arg arg = { 19738c2ecf20Sopenharmony_ci .dev = rt->dst.dev, 19748c2ecf20Sopenharmony_ci .gw = &rt->rt6i_gateway, 19758c2ecf20Sopenharmony_ci }; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (!arg.match) 19808c2ecf20Sopenharmony_ci goto unlock; 19818c2ecf20Sopenharmony_ci fib6_nh = arg.match; 19828c2ecf20Sopenharmony_ci } else { 19838c2ecf20Sopenharmony_ci fib6_nh = from->fib6_nh; 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt); 19868c2ecf20Sopenharmony_ciunlock: 19878c2ecf20Sopenharmony_ci rcu_read_unlock(); 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic bool rt6_mtu_change_route_allowed(struct inet6_dev *idev, 19918c2ecf20Sopenharmony_ci struct rt6_info *rt, int mtu) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci /* If the new MTU is lower than the route PMTU, this new MTU will be the 19948c2ecf20Sopenharmony_ci * lowest MTU in the path: always allow updating the route PMTU to 19958c2ecf20Sopenharmony_ci * reflect PMTU decreases. 19968c2ecf20Sopenharmony_ci * 19978c2ecf20Sopenharmony_ci * If the new MTU is higher, and the route PMTU is equal to the local 19988c2ecf20Sopenharmony_ci * MTU, this means the old MTU is the lowest in the path, so allow 19998c2ecf20Sopenharmony_ci * updating it: if other nodes now have lower MTUs, PMTU discovery will 20008c2ecf20Sopenharmony_ci * handle this. 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (dst_mtu(&rt->dst) >= mtu) 20048c2ecf20Sopenharmony_ci return true; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci if (dst_mtu(&rt->dst) == idev->cnf.mtu6) 20078c2ecf20Sopenharmony_ci return true; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci return false; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistatic void rt6_exceptions_update_pmtu(struct inet6_dev *idev, 20138c2ecf20Sopenharmony_ci const struct fib6_nh *nh, int mtu) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 20168c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 20178c2ecf20Sopenharmony_ci int i; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 20208c2ecf20Sopenharmony_ci if (!bucket) 20218c2ecf20Sopenharmony_ci return; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 20248c2ecf20Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 20258c2ecf20Sopenharmony_ci struct rt6_info *entry = rt6_ex->rt6i; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected 20288c2ecf20Sopenharmony_ci * route), the metrics of its rt->from have already 20298c2ecf20Sopenharmony_ci * been updated. 20308c2ecf20Sopenharmony_ci */ 20318c2ecf20Sopenharmony_ci if (dst_metric_raw(&entry->dst, RTAX_MTU) && 20328c2ecf20Sopenharmony_ci rt6_mtu_change_route_allowed(idev, entry, mtu)) 20338c2ecf20Sopenharmony_ci dst_metric_set(&entry->dst, RTAX_MTU, mtu); 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci bucket++; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci} 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_cistatic void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh, 20428c2ecf20Sopenharmony_ci const struct in6_addr *gateway) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 20458c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 20468c2ecf20Sopenharmony_ci struct hlist_node *tmp; 20478c2ecf20Sopenharmony_ci int i; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 20508c2ecf20Sopenharmony_ci return; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 20538c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 20548c2ecf20Sopenharmony_ci if (bucket) { 20558c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 20568c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, 20578c2ecf20Sopenharmony_ci &bucket->chain, hlist) { 20588c2ecf20Sopenharmony_ci struct rt6_info *entry = rt6_ex->rt6i; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) == 20618c2ecf20Sopenharmony_ci RTF_CACHE_GATEWAY && 20628c2ecf20Sopenharmony_ci ipv6_addr_equal(gateway, 20638c2ecf20Sopenharmony_ci &entry->rt6i_gateway)) { 20648c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci bucket++; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 20728c2ecf20Sopenharmony_ci} 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_cistatic void rt6_age_examine_exception(struct rt6_exception_bucket *bucket, 20758c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex, 20768c2ecf20Sopenharmony_ci struct fib6_gc_args *gc_args, 20778c2ecf20Sopenharmony_ci unsigned long now) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci struct rt6_info *rt = rt6_ex->rt6i; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci /* we are pruning and obsoleting aged-out and non gateway exceptions 20828c2ecf20Sopenharmony_ci * even if others have still references to them, so that on next 20838c2ecf20Sopenharmony_ci * dst_check() such references can be dropped. 20848c2ecf20Sopenharmony_ci * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when 20858c2ecf20Sopenharmony_ci * expired, independently from their aging, as per RFC 8201 section 4 20868c2ecf20Sopenharmony_ci */ 20878c2ecf20Sopenharmony_ci if (!(rt->rt6i_flags & RTF_EXPIRES)) { 20888c2ecf20Sopenharmony_ci if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) { 20898c2ecf20Sopenharmony_ci RT6_TRACE("aging clone %p\n", rt); 20908c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 20918c2ecf20Sopenharmony_ci return; 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci } else if (time_after(jiffies, rt->dst.expires)) { 20948c2ecf20Sopenharmony_ci RT6_TRACE("purging expired route %p\n", rt); 20958c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 20968c2ecf20Sopenharmony_ci return; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_GATEWAY) { 21008c2ecf20Sopenharmony_ci struct neighbour *neigh; 21018c2ecf20Sopenharmony_ci __u8 neigh_flags = 0; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 21048c2ecf20Sopenharmony_ci if (neigh) 21058c2ecf20Sopenharmony_ci neigh_flags = neigh->flags; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci if (!(neigh_flags & NTF_ROUTER)) { 21088c2ecf20Sopenharmony_ci RT6_TRACE("purging route %p via non-router but gateway\n", 21098c2ecf20Sopenharmony_ci rt); 21108c2ecf20Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 21118c2ecf20Sopenharmony_ci return; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci } 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci gc_args->more++; 21168c2ecf20Sopenharmony_ci} 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_cistatic void fib6_nh_age_exceptions(const struct fib6_nh *nh, 21198c2ecf20Sopenharmony_ci struct fib6_gc_args *gc_args, 21208c2ecf20Sopenharmony_ci unsigned long now) 21218c2ecf20Sopenharmony_ci{ 21228c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 21238c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 21248c2ecf20Sopenharmony_ci struct hlist_node *tmp; 21258c2ecf20Sopenharmony_ci int i; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 21288c2ecf20Sopenharmony_ci return; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 21318c2ecf20Sopenharmony_ci spin_lock(&rt6_exception_lock); 21328c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 21338c2ecf20Sopenharmony_ci if (bucket) { 21348c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 21358c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, 21368c2ecf20Sopenharmony_ci &bucket->chain, hlist) { 21378c2ecf20Sopenharmony_ci rt6_age_examine_exception(bucket, rt6_ex, 21388c2ecf20Sopenharmony_ci gc_args, now); 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci bucket++; 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci spin_unlock(&rt6_exception_lock); 21448c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 21458c2ecf20Sopenharmony_ci} 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_cistruct fib6_nh_age_excptn_arg { 21488c2ecf20Sopenharmony_ci struct fib6_gc_args *gc_args; 21498c2ecf20Sopenharmony_ci unsigned long now; 21508c2ecf20Sopenharmony_ci}; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci struct fib6_nh_age_excptn_arg *arg = _arg; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci fib6_nh_age_exceptions(nh, arg->gc_args, arg->now); 21578c2ecf20Sopenharmony_ci return 0; 21588c2ecf20Sopenharmony_ci} 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_civoid rt6_age_exceptions(struct fib6_info *f6i, 21618c2ecf20Sopenharmony_ci struct fib6_gc_args *gc_args, 21628c2ecf20Sopenharmony_ci unsigned long now) 21638c2ecf20Sopenharmony_ci{ 21648c2ecf20Sopenharmony_ci if (f6i->nh) { 21658c2ecf20Sopenharmony_ci struct fib6_nh_age_excptn_arg arg = { 21668c2ecf20Sopenharmony_ci .gc_args = gc_args, 21678c2ecf20Sopenharmony_ci .now = now 21688c2ecf20Sopenharmony_ci }; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions, 21718c2ecf20Sopenharmony_ci &arg); 21728c2ecf20Sopenharmony_ci } else { 21738c2ecf20Sopenharmony_ci fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now); 21748c2ecf20Sopenharmony_ci } 21758c2ecf20Sopenharmony_ci} 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci/* must be called with rcu lock held */ 21788c2ecf20Sopenharmony_ciint fib6_table_lookup(struct net *net, struct fib6_table *table, int oif, 21798c2ecf20Sopenharmony_ci struct flowi6 *fl6, struct fib6_result *res, int strict) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci struct fib6_node *fn, *saved_fn; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 21848c2ecf20Sopenharmony_ci saved_fn = fn; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 21878c2ecf20Sopenharmony_ci oif = 0; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ciredo_rt6_select: 21908c2ecf20Sopenharmony_ci rt6_select(net, fn, oif, res, strict); 21918c2ecf20Sopenharmony_ci if (res->f6i == net->ipv6.fib6_null_entry) { 21928c2ecf20Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 21938c2ecf20Sopenharmony_ci if (fn) 21948c2ecf20Sopenharmony_ci goto redo_rt6_select; 21958c2ecf20Sopenharmony_ci else if (strict & RT6_LOOKUP_F_REACHABLE) { 21968c2ecf20Sopenharmony_ci /* also consider unreachable route */ 21978c2ecf20Sopenharmony_ci strict &= ~RT6_LOOKUP_F_REACHABLE; 21988c2ecf20Sopenharmony_ci fn = saved_fn; 21998c2ecf20Sopenharmony_ci goto redo_rt6_select; 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci trace_fib6_table_lookup(net, res, table, fl6); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci return 0; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistruct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 22098c2ecf20Sopenharmony_ci int oif, struct flowi6 *fl6, 22108c2ecf20Sopenharmony_ci const struct sk_buff *skb, int flags) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci struct fib6_result res = {}; 22138c2ecf20Sopenharmony_ci struct rt6_info *rt = NULL; 22148c2ecf20Sopenharmony_ci int strict = 0; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) && 22178c2ecf20Sopenharmony_ci !rcu_read_lock_held()); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci strict |= flags & RT6_LOOKUP_F_IFACE; 22208c2ecf20Sopenharmony_ci strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; 22218c2ecf20Sopenharmony_ci if (net->ipv6.devconf_all->forwarding == 0) 22228c2ecf20Sopenharmony_ci strict |= RT6_LOOKUP_F_REACHABLE; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci rcu_read_lock(); 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci fib6_table_lookup(net, table, oif, fl6, &res, strict); 22278c2ecf20Sopenharmony_ci if (res.f6i == net->ipv6.fib6_null_entry) 22288c2ecf20Sopenharmony_ci goto out; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci fib6_select_path(net, &res, fl6, oif, false, skb, strict); 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci /*Search through exception table */ 22338c2ecf20Sopenharmony_ci rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); 22348c2ecf20Sopenharmony_ci if (rt) { 22358c2ecf20Sopenharmony_ci goto out; 22368c2ecf20Sopenharmony_ci } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 22378c2ecf20Sopenharmony_ci !res.nh->fib_nh_gw_family)) { 22388c2ecf20Sopenharmony_ci /* Create a RTF_CACHE clone which will not be 22398c2ecf20Sopenharmony_ci * owned by the fib6 tree. It is for the special case where 22408c2ecf20Sopenharmony_ci * the daddr in the skb during the neighbor look-up is different 22418c2ecf20Sopenharmony_ci * from the fl6->daddr used to look-up route here. 22428c2ecf20Sopenharmony_ci */ 22438c2ecf20Sopenharmony_ci rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci if (rt) { 22468c2ecf20Sopenharmony_ci /* 1 refcnt is taken during ip6_rt_cache_alloc(). 22478c2ecf20Sopenharmony_ci * As rt6_uncached_list_add() does not consume refcnt, 22488c2ecf20Sopenharmony_ci * this refcnt is always returned to the caller even 22498c2ecf20Sopenharmony_ci * if caller sets RT6_LOOKUP_F_DST_NOREF flag. 22508c2ecf20Sopenharmony_ci */ 22518c2ecf20Sopenharmony_ci rt6_uncached_list_add(rt); 22528c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 22538c2ecf20Sopenharmony_ci rcu_read_unlock(); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci return rt; 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci } else { 22588c2ecf20Sopenharmony_ci /* Get a percpu copy */ 22598c2ecf20Sopenharmony_ci local_bh_disable(); 22608c2ecf20Sopenharmony_ci rt = rt6_get_pcpu_route(&res); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci if (!rt) 22638c2ecf20Sopenharmony_ci rt = rt6_make_pcpu_route(net, &res); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci local_bh_enable(); 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ciout: 22688c2ecf20Sopenharmony_ci if (!rt) 22698c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 22708c2ecf20Sopenharmony_ci if (!(flags & RT6_LOOKUP_F_DST_NOREF)) 22718c2ecf20Sopenharmony_ci ip6_hold_safe(net, &rt); 22728c2ecf20Sopenharmony_ci rcu_read_unlock(); 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci return rt; 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_pol_route); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net, 22798c2ecf20Sopenharmony_ci struct fib6_table *table, 22808c2ecf20Sopenharmony_ci struct flowi6 *fl6, 22818c2ecf20Sopenharmony_ci const struct sk_buff *skb, 22828c2ecf20Sopenharmony_ci int flags) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags); 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_input_lookup(struct net *net, 22888c2ecf20Sopenharmony_ci struct net_device *dev, 22898c2ecf20Sopenharmony_ci struct flowi6 *fl6, 22908c2ecf20Sopenharmony_ci const struct sk_buff *skb, 22918c2ecf20Sopenharmony_ci int flags) 22928c2ecf20Sopenharmony_ci{ 22938c2ecf20Sopenharmony_ci if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 22948c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_IFACE; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input); 22978c2ecf20Sopenharmony_ci} 22988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_input_lookup); 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_cistatic void ip6_multipath_l3_keys(const struct sk_buff *skb, 23018c2ecf20Sopenharmony_ci struct flow_keys *keys, 23028c2ecf20Sopenharmony_ci struct flow_keys *flkeys) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci const struct ipv6hdr *outer_iph = ipv6_hdr(skb); 23058c2ecf20Sopenharmony_ci const struct ipv6hdr *key_iph = outer_iph; 23068c2ecf20Sopenharmony_ci struct flow_keys *_flkeys = flkeys; 23078c2ecf20Sopenharmony_ci const struct ipv6hdr *inner_iph; 23088c2ecf20Sopenharmony_ci const struct icmp6hdr *icmph; 23098c2ecf20Sopenharmony_ci struct ipv6hdr _inner_iph; 23108c2ecf20Sopenharmony_ci struct icmp6hdr _icmph; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6)) 23138c2ecf20Sopenharmony_ci goto out; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci icmph = skb_header_pointer(skb, skb_transport_offset(skb), 23168c2ecf20Sopenharmony_ci sizeof(_icmph), &_icmph); 23178c2ecf20Sopenharmony_ci if (!icmph) 23188c2ecf20Sopenharmony_ci goto out; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (!icmpv6_is_err(icmph->icmp6_type)) 23218c2ecf20Sopenharmony_ci goto out; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci inner_iph = skb_header_pointer(skb, 23248c2ecf20Sopenharmony_ci skb_transport_offset(skb) + sizeof(*icmph), 23258c2ecf20Sopenharmony_ci sizeof(_inner_iph), &_inner_iph); 23268c2ecf20Sopenharmony_ci if (!inner_iph) 23278c2ecf20Sopenharmony_ci goto out; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci key_iph = inner_iph; 23308c2ecf20Sopenharmony_ci _flkeys = NULL; 23318c2ecf20Sopenharmony_ciout: 23328c2ecf20Sopenharmony_ci if (_flkeys) { 23338c2ecf20Sopenharmony_ci keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src; 23348c2ecf20Sopenharmony_ci keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst; 23358c2ecf20Sopenharmony_ci keys->tags.flow_label = _flkeys->tags.flow_label; 23368c2ecf20Sopenharmony_ci keys->basic.ip_proto = _flkeys->basic.ip_proto; 23378c2ecf20Sopenharmony_ci } else { 23388c2ecf20Sopenharmony_ci keys->addrs.v6addrs.src = key_iph->saddr; 23398c2ecf20Sopenharmony_ci keys->addrs.v6addrs.dst = key_iph->daddr; 23408c2ecf20Sopenharmony_ci keys->tags.flow_label = ip6_flowlabel(key_iph); 23418c2ecf20Sopenharmony_ci keys->basic.ip_proto = key_iph->nexthdr; 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci} 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci/* if skb is set it will be used and fl6 can be NULL */ 23468c2ecf20Sopenharmony_ciu32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, 23478c2ecf20Sopenharmony_ci const struct sk_buff *skb, struct flow_keys *flkeys) 23488c2ecf20Sopenharmony_ci{ 23498c2ecf20Sopenharmony_ci struct flow_keys hash_keys; 23508c2ecf20Sopenharmony_ci u32 mhash; 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci switch (ip6_multipath_hash_policy(net)) { 23538c2ecf20Sopenharmony_ci case 0: 23548c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 23558c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 23568c2ecf20Sopenharmony_ci if (skb) { 23578c2ecf20Sopenharmony_ci ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 23588c2ecf20Sopenharmony_ci } else { 23598c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 23608c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 23618c2ecf20Sopenharmony_ci hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); 23628c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci break; 23658c2ecf20Sopenharmony_ci case 1: 23668c2ecf20Sopenharmony_ci if (skb) { 23678c2ecf20Sopenharmony_ci unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; 23688c2ecf20Sopenharmony_ci struct flow_keys keys; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci /* short-circuit if we already have L4 hash present */ 23718c2ecf20Sopenharmony_ci if (skb->l4_hash) 23728c2ecf20Sopenharmony_ci return skb_get_hash_raw(skb) >> 1; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci if (!flkeys) { 23778c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, flag); 23788c2ecf20Sopenharmony_ci flkeys = &keys; 23798c2ecf20Sopenharmony_ci } 23808c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 23818c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 23828c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 23838c2ecf20Sopenharmony_ci hash_keys.ports.src = flkeys->ports.src; 23848c2ecf20Sopenharmony_ci hash_keys.ports.dst = flkeys->ports.dst; 23858c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 23868c2ecf20Sopenharmony_ci } else { 23878c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 23888c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 23898c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 23908c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 23918c2ecf20Sopenharmony_ci hash_keys.ports.src = fl6->fl6_sport; 23928c2ecf20Sopenharmony_ci hash_keys.ports.dst = fl6->fl6_dport; 23938c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 23948c2ecf20Sopenharmony_ci } 23958c2ecf20Sopenharmony_ci break; 23968c2ecf20Sopenharmony_ci case 2: 23978c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 23988c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 23998c2ecf20Sopenharmony_ci if (skb) { 24008c2ecf20Sopenharmony_ci struct flow_keys keys; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci if (!flkeys) { 24038c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, 0); 24048c2ecf20Sopenharmony_ci flkeys = &keys; 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* Inner can be v4 or v6 */ 24088c2ecf20Sopenharmony_ci if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 24098c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 24108c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src; 24118c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst; 24128c2ecf20Sopenharmony_ci } else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 24138c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 24148c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 24158c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 24168c2ecf20Sopenharmony_ci hash_keys.tags.flow_label = flkeys->tags.flow_label; 24178c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 24188c2ecf20Sopenharmony_ci } else { 24198c2ecf20Sopenharmony_ci /* Same as case 0 */ 24208c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 24218c2ecf20Sopenharmony_ci ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 24228c2ecf20Sopenharmony_ci } 24238c2ecf20Sopenharmony_ci } else { 24248c2ecf20Sopenharmony_ci /* Same as case 0 */ 24258c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 24268c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 24278c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 24288c2ecf20Sopenharmony_ci hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); 24298c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 24308c2ecf20Sopenharmony_ci } 24318c2ecf20Sopenharmony_ci break; 24328c2ecf20Sopenharmony_ci } 24338c2ecf20Sopenharmony_ci mhash = flow_hash_from_keys(&hash_keys); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci return mhash >> 1; 24368c2ecf20Sopenharmony_ci} 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci/* Called with rcu held */ 24398c2ecf20Sopenharmony_civoid ip6_route_input(struct sk_buff *skb) 24408c2ecf20Sopenharmony_ci{ 24418c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 24428c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 24438c2ecf20Sopenharmony_ci int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF; 24448c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info; 24458c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 24468c2ecf20Sopenharmony_ci .flowi6_iif = skb->dev->ifindex, 24478c2ecf20Sopenharmony_ci .daddr = iph->daddr, 24488c2ecf20Sopenharmony_ci .saddr = iph->saddr, 24498c2ecf20Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 24508c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 24518c2ecf20Sopenharmony_ci .flowi6_proto = iph->nexthdr, 24528c2ecf20Sopenharmony_ci }; 24538c2ecf20Sopenharmony_ci struct flow_keys *flkeys = NULL, _flkeys; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info(skb); 24568c2ecf20Sopenharmony_ci if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 24578c2ecf20Sopenharmony_ci fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys)) 24608c2ecf20Sopenharmony_ci flkeys = &_flkeys; 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) 24638c2ecf20Sopenharmony_ci fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); 24648c2ecf20Sopenharmony_ci skb_dst_drop(skb); 24658c2ecf20Sopenharmony_ci skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev, 24668c2ecf20Sopenharmony_ci &fl6, skb, flags)); 24678c2ecf20Sopenharmony_ci} 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net, 24708c2ecf20Sopenharmony_ci struct fib6_table *table, 24718c2ecf20Sopenharmony_ci struct flowi6 *fl6, 24728c2ecf20Sopenharmony_ci const struct sk_buff *skb, 24738c2ecf20Sopenharmony_ci int flags) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); 24768c2ecf20Sopenharmony_ci} 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_output_flags_noref(struct net *net, 24798c2ecf20Sopenharmony_ci const struct sock *sk, 24808c2ecf20Sopenharmony_ci struct flowi6 *fl6, int flags) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci bool any_src; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci if (ipv6_addr_type(&fl6->daddr) & 24858c2ecf20Sopenharmony_ci (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) { 24868c2ecf20Sopenharmony_ci struct dst_entry *dst; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci /* This function does not take refcnt on the dst */ 24898c2ecf20Sopenharmony_ci dst = l3mdev_link_scope_lookup(net, fl6); 24908c2ecf20Sopenharmony_ci if (dst) 24918c2ecf20Sopenharmony_ci return dst; 24928c2ecf20Sopenharmony_ci } 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci fl6->flowi6_iif = LOOPBACK_IFINDEX; 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_DST_NOREF; 24978c2ecf20Sopenharmony_ci any_src = ipv6_addr_any(&fl6->saddr); 24988c2ecf20Sopenharmony_ci if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 24998c2ecf20Sopenharmony_ci (fl6->flowi6_oif && any_src)) 25008c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_IFACE; 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci if (!any_src) 25038c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 25048c2ecf20Sopenharmony_ci else if (sk) 25058c2ecf20Sopenharmony_ci flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); 25088c2ecf20Sopenharmony_ci} 25098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags_noref); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_cistruct dst_entry *ip6_route_output_flags(struct net *net, 25128c2ecf20Sopenharmony_ci const struct sock *sk, 25138c2ecf20Sopenharmony_ci struct flowi6 *fl6, 25148c2ecf20Sopenharmony_ci int flags) 25158c2ecf20Sopenharmony_ci{ 25168c2ecf20Sopenharmony_ci struct dst_entry *dst; 25178c2ecf20Sopenharmony_ci struct rt6_info *rt6; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci rcu_read_lock(); 25208c2ecf20Sopenharmony_ci dst = ip6_route_output_flags_noref(net, sk, fl6, flags); 25218c2ecf20Sopenharmony_ci rt6 = (struct rt6_info *)dst; 25228c2ecf20Sopenharmony_ci /* For dst cached in uncached_list, refcnt is already taken. */ 25238c2ecf20Sopenharmony_ci if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) { 25248c2ecf20Sopenharmony_ci dst = &net->ipv6.ip6_null_entry->dst; 25258c2ecf20Sopenharmony_ci dst_hold(dst); 25268c2ecf20Sopenharmony_ci } 25278c2ecf20Sopenharmony_ci rcu_read_unlock(); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci return dst; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags); 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_cistruct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 25368c2ecf20Sopenharmony_ci struct net_device *loopback_dev = net->loopback_dev; 25378c2ecf20Sopenharmony_ci struct dst_entry *new = NULL; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1, 25408c2ecf20Sopenharmony_ci DST_OBSOLETE_DEAD, 0); 25418c2ecf20Sopenharmony_ci if (rt) { 25428c2ecf20Sopenharmony_ci rt6_info_init(rt); 25438c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci new = &rt->dst; 25468c2ecf20Sopenharmony_ci new->__use = 1; 25478c2ecf20Sopenharmony_ci new->input = dst_discard; 25488c2ecf20Sopenharmony_ci new->output = dst_discard_out; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci dst_copy_metrics(new, &ort->dst); 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci rt->rt6i_idev = in6_dev_get(loopback_dev); 25538c2ecf20Sopenharmony_ci rt->rt6i_gateway = ort->rt6i_gateway; 25548c2ecf20Sopenharmony_ci rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 25578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 25588c2ecf20Sopenharmony_ci memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 25598c2ecf20Sopenharmony_ci#endif 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci dst_release(dst_orig); 25638c2ecf20Sopenharmony_ci return new ? new : ERR_PTR(-ENOMEM); 25648c2ecf20Sopenharmony_ci} 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci/* 25678c2ecf20Sopenharmony_ci * Destination cache support functions 25688c2ecf20Sopenharmony_ci */ 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_cistatic bool fib6_check(struct fib6_info *f6i, u32 cookie) 25718c2ecf20Sopenharmony_ci{ 25728c2ecf20Sopenharmony_ci u32 rt_cookie = 0; 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie) 25758c2ecf20Sopenharmony_ci return false; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci if (fib6_check_expired(f6i)) 25788c2ecf20Sopenharmony_ci return false; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci return true; 25818c2ecf20Sopenharmony_ci} 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_cistatic struct dst_entry *rt6_check(struct rt6_info *rt, 25848c2ecf20Sopenharmony_ci struct fib6_info *from, 25858c2ecf20Sopenharmony_ci u32 cookie) 25868c2ecf20Sopenharmony_ci{ 25878c2ecf20Sopenharmony_ci u32 rt_cookie = 0; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci if (!from || !fib6_get_cookie_safe(from, &rt_cookie) || 25908c2ecf20Sopenharmony_ci rt_cookie != cookie) 25918c2ecf20Sopenharmony_ci return NULL; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if (rt6_check_expired(rt)) 25948c2ecf20Sopenharmony_ci return NULL; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci return &rt->dst; 25978c2ecf20Sopenharmony_ci} 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_cistatic struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, 26008c2ecf20Sopenharmony_ci struct fib6_info *from, 26018c2ecf20Sopenharmony_ci u32 cookie) 26028c2ecf20Sopenharmony_ci{ 26038c2ecf20Sopenharmony_ci if (!__rt6_check_expired(rt) && 26048c2ecf20Sopenharmony_ci rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 26058c2ecf20Sopenharmony_ci fib6_check(from, cookie)) 26068c2ecf20Sopenharmony_ci return &rt->dst; 26078c2ecf20Sopenharmony_ci else 26088c2ecf20Sopenharmony_ci return NULL; 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_cistatic struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 26128c2ecf20Sopenharmony_ci{ 26138c2ecf20Sopenharmony_ci struct dst_entry *dst_ret; 26148c2ecf20Sopenharmony_ci struct fib6_info *from; 26158c2ecf20Sopenharmony_ci struct rt6_info *rt; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci rt = container_of(dst, struct rt6_info, dst); 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci if (rt->sernum) 26208c2ecf20Sopenharmony_ci return rt6_is_valid(rt) ? dst : NULL; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci rcu_read_lock(); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci /* All IPV6 dsts are created with ->obsolete set to the value 26258c2ecf20Sopenharmony_ci * DST_OBSOLETE_FORCE_CHK which forces validation calls down 26268c2ecf20Sopenharmony_ci * into this function always. 26278c2ecf20Sopenharmony_ci */ 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci if (from && (rt->rt6i_flags & RTF_PCPU || 26328c2ecf20Sopenharmony_ci unlikely(!list_empty(&rt->rt6i_uncached)))) 26338c2ecf20Sopenharmony_ci dst_ret = rt6_dst_from_check(rt, from, cookie); 26348c2ecf20Sopenharmony_ci else 26358c2ecf20Sopenharmony_ci dst_ret = rt6_check(rt, from, cookie); 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci rcu_read_unlock(); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci return dst_ret; 26408c2ecf20Sopenharmony_ci} 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_cistatic void ip6_negative_advice(struct sock *sk, 26438c2ecf20Sopenharmony_ci struct dst_entry *dst) 26448c2ecf20Sopenharmony_ci{ 26458c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *) dst; 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_CACHE) { 26488c2ecf20Sopenharmony_ci rcu_read_lock(); 26498c2ecf20Sopenharmony_ci if (rt6_check_expired(rt)) { 26508c2ecf20Sopenharmony_ci /* counteract the dst_release() in sk_dst_reset() */ 26518c2ecf20Sopenharmony_ci dst_hold(dst); 26528c2ecf20Sopenharmony_ci sk_dst_reset(sk); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci rt6_remove_exception_rt(rt); 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci rcu_read_unlock(); 26578c2ecf20Sopenharmony_ci return; 26588c2ecf20Sopenharmony_ci } 26598c2ecf20Sopenharmony_ci sk_dst_reset(sk); 26608c2ecf20Sopenharmony_ci} 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb) 26638c2ecf20Sopenharmony_ci{ 26648c2ecf20Sopenharmony_ci struct rt6_info *rt; 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci rt = (struct rt6_info *) skb_dst(skb); 26698c2ecf20Sopenharmony_ci if (rt) { 26708c2ecf20Sopenharmony_ci rcu_read_lock(); 26718c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_CACHE) { 26728c2ecf20Sopenharmony_ci rt6_remove_exception_rt(rt); 26738c2ecf20Sopenharmony_ci } else { 26748c2ecf20Sopenharmony_ci struct fib6_info *from; 26758c2ecf20Sopenharmony_ci struct fib6_node *fn; 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 26788c2ecf20Sopenharmony_ci if (from) { 26798c2ecf20Sopenharmony_ci fn = rcu_dereference(from->fib6_node); 26808c2ecf20Sopenharmony_ci if (fn && (rt->rt6i_flags & RTF_DEFAULT)) 26818c2ecf20Sopenharmony_ci WRITE_ONCE(fn->fn_sernum, -1); 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci } 26848c2ecf20Sopenharmony_ci rcu_read_unlock(); 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci} 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_cistatic void rt6_update_expires(struct rt6_info *rt0, int timeout) 26898c2ecf20Sopenharmony_ci{ 26908c2ecf20Sopenharmony_ci if (!(rt0->rt6i_flags & RTF_EXPIRES)) { 26918c2ecf20Sopenharmony_ci struct fib6_info *from; 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci rcu_read_lock(); 26948c2ecf20Sopenharmony_ci from = rcu_dereference(rt0->from); 26958c2ecf20Sopenharmony_ci if (from) 26968c2ecf20Sopenharmony_ci rt0->dst.expires = from->expires; 26978c2ecf20Sopenharmony_ci rcu_read_unlock(); 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci dst_set_expires(&rt0->dst, timeout); 27018c2ecf20Sopenharmony_ci rt0->rt6i_flags |= RTF_EXPIRES; 27028c2ecf20Sopenharmony_ci} 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_cistatic void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct net *net = dev_net(rt->dst.dev); 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci dst_metric_set(&rt->dst, RTAX_MTU, mtu); 27098c2ecf20Sopenharmony_ci rt->rt6i_flags |= RTF_MODIFIED; 27108c2ecf20Sopenharmony_ci rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 27118c2ecf20Sopenharmony_ci} 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_cistatic bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 27148c2ecf20Sopenharmony_ci{ 27158c2ecf20Sopenharmony_ci return !(rt->rt6i_flags & RTF_CACHE) && 27168c2ecf20Sopenharmony_ci (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from)); 27178c2ecf20Sopenharmony_ci} 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_cistatic void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 27208c2ecf20Sopenharmony_ci const struct ipv6hdr *iph, u32 mtu, 27218c2ecf20Sopenharmony_ci bool confirm_neigh) 27228c2ecf20Sopenharmony_ci{ 27238c2ecf20Sopenharmony_ci const struct in6_addr *daddr, *saddr; 27248c2ecf20Sopenharmony_ci struct rt6_info *rt6 = (struct rt6_info *)dst; 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci /* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU) 27278c2ecf20Sopenharmony_ci * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it. 27288c2ecf20Sopenharmony_ci * [see also comment in rt6_mtu_change_route()] 27298c2ecf20Sopenharmony_ci */ 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci if (iph) { 27328c2ecf20Sopenharmony_ci daddr = &iph->daddr; 27338c2ecf20Sopenharmony_ci saddr = &iph->saddr; 27348c2ecf20Sopenharmony_ci } else if (sk) { 27358c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 27368c2ecf20Sopenharmony_ci saddr = &inet6_sk(sk)->saddr; 27378c2ecf20Sopenharmony_ci } else { 27388c2ecf20Sopenharmony_ci daddr = NULL; 27398c2ecf20Sopenharmony_ci saddr = NULL; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci if (confirm_neigh) 27438c2ecf20Sopenharmony_ci dst_confirm_neigh(dst, daddr); 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 27468c2ecf20Sopenharmony_ci return; 27478c2ecf20Sopenharmony_ci if (mtu >= dst_mtu(dst)) 27488c2ecf20Sopenharmony_ci return; 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci if (!rt6_cache_allowed_for_pmtu(rt6)) { 27518c2ecf20Sopenharmony_ci rt6_do_update_pmtu(rt6, mtu); 27528c2ecf20Sopenharmony_ci /* update rt6_ex->stamp for cache */ 27538c2ecf20Sopenharmony_ci if (rt6->rt6i_flags & RTF_CACHE) 27548c2ecf20Sopenharmony_ci rt6_update_exception_stamp_rt(rt6); 27558c2ecf20Sopenharmony_ci } else if (daddr) { 27568c2ecf20Sopenharmony_ci struct fib6_result res = {}; 27578c2ecf20Sopenharmony_ci struct rt6_info *nrt6; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci rcu_read_lock(); 27608c2ecf20Sopenharmony_ci res.f6i = rcu_dereference(rt6->from); 27618c2ecf20Sopenharmony_ci if (!res.f6i) 27628c2ecf20Sopenharmony_ci goto out_unlock; 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 27658c2ecf20Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci if (res.f6i->nh) { 27688c2ecf20Sopenharmony_ci struct fib6_nh_match_arg arg = { 27698c2ecf20Sopenharmony_ci .dev = dst->dev, 27708c2ecf20Sopenharmony_ci .gw = &rt6->rt6i_gateway, 27718c2ecf20Sopenharmony_ci }; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(res.f6i->nh, 27748c2ecf20Sopenharmony_ci fib6_nh_find_match, &arg); 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci /* fib6_info uses a nexthop that does not have fib6_nh 27778c2ecf20Sopenharmony_ci * using the dst->dev + gw. Should be impossible. 27788c2ecf20Sopenharmony_ci */ 27798c2ecf20Sopenharmony_ci if (!arg.match) 27808c2ecf20Sopenharmony_ci goto out_unlock; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci res.nh = arg.match; 27838c2ecf20Sopenharmony_ci } else { 27848c2ecf20Sopenharmony_ci res.nh = res.f6i->fib6_nh; 27858c2ecf20Sopenharmony_ci } 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr); 27888c2ecf20Sopenharmony_ci if (nrt6) { 27898c2ecf20Sopenharmony_ci rt6_do_update_pmtu(nrt6, mtu); 27908c2ecf20Sopenharmony_ci if (rt6_insert_exception(nrt6, &res)) 27918c2ecf20Sopenharmony_ci dst_release_immediate(&nrt6->dst); 27928c2ecf20Sopenharmony_ci } 27938c2ecf20Sopenharmony_ciout_unlock: 27948c2ecf20Sopenharmony_ci rcu_read_unlock(); 27958c2ecf20Sopenharmony_ci } 27968c2ecf20Sopenharmony_ci} 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 27998c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 mtu, 28008c2ecf20Sopenharmony_ci bool confirm_neigh) 28018c2ecf20Sopenharmony_ci{ 28028c2ecf20Sopenharmony_ci __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu, 28038c2ecf20Sopenharmony_ci confirm_neigh); 28048c2ecf20Sopenharmony_ci} 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_civoid ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 28078c2ecf20Sopenharmony_ci int oif, u32 mark, kuid_t uid) 28088c2ecf20Sopenharmony_ci{ 28098c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 28108c2ecf20Sopenharmony_ci struct dst_entry *dst; 28118c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 28128c2ecf20Sopenharmony_ci .flowi6_oif = oif, 28138c2ecf20Sopenharmony_ci .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark), 28148c2ecf20Sopenharmony_ci .daddr = iph->daddr, 28158c2ecf20Sopenharmony_ci .saddr = iph->saddr, 28168c2ecf20Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 28178c2ecf20Sopenharmony_ci .flowi6_uid = uid, 28188c2ecf20Sopenharmony_ci }; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 28218c2ecf20Sopenharmony_ci if (!dst->error) 28228c2ecf20Sopenharmony_ci __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true); 28238c2ecf20Sopenharmony_ci dst_release(dst); 28248c2ecf20Sopenharmony_ci} 28258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_update_pmtu); 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_civoid ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 28288c2ecf20Sopenharmony_ci{ 28298c2ecf20Sopenharmony_ci int oif = sk->sk_bound_dev_if; 28308c2ecf20Sopenharmony_ci struct dst_entry *dst; 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci if (!oif && skb->dev) 28338c2ecf20Sopenharmony_ci oif = l3mdev_master_ifindex(skb->dev); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid); 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci dst = __sk_dst_get(sk); 28388c2ecf20Sopenharmony_ci if (!dst || !dst->obsolete || 28398c2ecf20Sopenharmony_ci dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 28408c2ecf20Sopenharmony_ci return; 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci bh_lock_sock(sk); 28438c2ecf20Sopenharmony_ci if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 28448c2ecf20Sopenharmony_ci ip6_datagram_dst_update(sk, false); 28458c2ecf20Sopenharmony_ci bh_unlock_sock(sk); 28468c2ecf20Sopenharmony_ci} 28478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_civoid ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, 28508c2ecf20Sopenharmony_ci const struct flowi6 *fl6) 28518c2ecf20Sopenharmony_ci{ 28528c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 28538c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 28548c2ecf20Sopenharmony_ci#endif 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci ip6_dst_store(sk, dst, 28578c2ecf20Sopenharmony_ci ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ? 28588c2ecf20Sopenharmony_ci &sk->sk_v6_daddr : NULL, 28598c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 28608c2ecf20Sopenharmony_ci ipv6_addr_equal(&fl6->saddr, &np->saddr) ? 28618c2ecf20Sopenharmony_ci &np->saddr : 28628c2ecf20Sopenharmony_ci#endif 28638c2ecf20Sopenharmony_ci NULL); 28648c2ecf20Sopenharmony_ci} 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_cistatic bool ip6_redirect_nh_match(const struct fib6_result *res, 28678c2ecf20Sopenharmony_ci struct flowi6 *fl6, 28688c2ecf20Sopenharmony_ci const struct in6_addr *gw, 28698c2ecf20Sopenharmony_ci struct rt6_info **ret) 28708c2ecf20Sopenharmony_ci{ 28718c2ecf20Sopenharmony_ci const struct fib6_nh *nh = res->nh; 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family || 28748c2ecf20Sopenharmony_ci fl6->flowi6_oif != nh->fib_nh_dev->ifindex) 28758c2ecf20Sopenharmony_ci return false; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci /* rt_cache's gateway might be different from its 'parent' 28788c2ecf20Sopenharmony_ci * in the case of an ip redirect. 28798c2ecf20Sopenharmony_ci * So we keep searching in the exception table if the gateway 28808c2ecf20Sopenharmony_ci * is different. 28818c2ecf20Sopenharmony_ci */ 28828c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) { 28838c2ecf20Sopenharmony_ci struct rt6_info *rt_cache; 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr); 28868c2ecf20Sopenharmony_ci if (rt_cache && 28878c2ecf20Sopenharmony_ci ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) { 28888c2ecf20Sopenharmony_ci *ret = rt_cache; 28898c2ecf20Sopenharmony_ci return true; 28908c2ecf20Sopenharmony_ci } 28918c2ecf20Sopenharmony_ci return false; 28928c2ecf20Sopenharmony_ci } 28938c2ecf20Sopenharmony_ci return true; 28948c2ecf20Sopenharmony_ci} 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_cistruct fib6_nh_rd_arg { 28978c2ecf20Sopenharmony_ci struct fib6_result *res; 28988c2ecf20Sopenharmony_ci struct flowi6 *fl6; 28998c2ecf20Sopenharmony_ci const struct in6_addr *gw; 29008c2ecf20Sopenharmony_ci struct rt6_info **ret; 29018c2ecf20Sopenharmony_ci}; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_cistatic int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg) 29048c2ecf20Sopenharmony_ci{ 29058c2ecf20Sopenharmony_ci struct fib6_nh_rd_arg *arg = _arg; 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci arg->res->nh = nh; 29088c2ecf20Sopenharmony_ci return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret); 29098c2ecf20Sopenharmony_ci} 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci/* Handle redirects */ 29128c2ecf20Sopenharmony_cistruct ip6rd_flowi { 29138c2ecf20Sopenharmony_ci struct flowi6 fl6; 29148c2ecf20Sopenharmony_ci struct in6_addr gateway; 29158c2ecf20Sopenharmony_ci}; 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net, 29188c2ecf20Sopenharmony_ci struct fib6_table *table, 29198c2ecf20Sopenharmony_ci struct flowi6 *fl6, 29208c2ecf20Sopenharmony_ci const struct sk_buff *skb, 29218c2ecf20Sopenharmony_ci int flags) 29228c2ecf20Sopenharmony_ci{ 29238c2ecf20Sopenharmony_ci struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 29248c2ecf20Sopenharmony_ci struct rt6_info *ret = NULL; 29258c2ecf20Sopenharmony_ci struct fib6_result res = {}; 29268c2ecf20Sopenharmony_ci struct fib6_nh_rd_arg arg = { 29278c2ecf20Sopenharmony_ci .res = &res, 29288c2ecf20Sopenharmony_ci .fl6 = fl6, 29298c2ecf20Sopenharmony_ci .gw = &rdfl->gateway, 29308c2ecf20Sopenharmony_ci .ret = &ret 29318c2ecf20Sopenharmony_ci }; 29328c2ecf20Sopenharmony_ci struct fib6_info *rt; 29338c2ecf20Sopenharmony_ci struct fib6_node *fn; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci /* l3mdev_update_flow overrides oif if the device is enslaved; in 29368c2ecf20Sopenharmony_ci * this case we must match on the real ingress device, so reset it 29378c2ecf20Sopenharmony_ci */ 29388c2ecf20Sopenharmony_ci if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 29398c2ecf20Sopenharmony_ci fl6->flowi6_oif = skb->dev->ifindex; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci /* Get the "current" route for this destination and 29428c2ecf20Sopenharmony_ci * check if the redirect has come from appropriate router. 29438c2ecf20Sopenharmony_ci * 29448c2ecf20Sopenharmony_ci * RFC 4861 specifies that redirects should only be 29458c2ecf20Sopenharmony_ci * accepted if they come from the nexthop to the target. 29468c2ecf20Sopenharmony_ci * Due to the way the routes are chosen, this notion 29478c2ecf20Sopenharmony_ci * is a bit fuzzy and one might need to check all possible 29488c2ecf20Sopenharmony_ci * routes. 29498c2ecf20Sopenharmony_ci */ 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci rcu_read_lock(); 29528c2ecf20Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 29538c2ecf20Sopenharmony_cirestart: 29548c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 29558c2ecf20Sopenharmony_ci res.f6i = rt; 29568c2ecf20Sopenharmony_ci if (fib6_check_expired(rt)) 29578c2ecf20Sopenharmony_ci continue; 29588c2ecf20Sopenharmony_ci if (rt->fib6_flags & RTF_REJECT) 29598c2ecf20Sopenharmony_ci break; 29608c2ecf20Sopenharmony_ci if (unlikely(rt->nh)) { 29618c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(rt->nh)) 29628c2ecf20Sopenharmony_ci continue; 29638c2ecf20Sopenharmony_ci /* on match, res->nh is filled in and potentially ret */ 29648c2ecf20Sopenharmony_ci if (nexthop_for_each_fib6_nh(rt->nh, 29658c2ecf20Sopenharmony_ci fib6_nh_redirect_match, 29668c2ecf20Sopenharmony_ci &arg)) 29678c2ecf20Sopenharmony_ci goto out; 29688c2ecf20Sopenharmony_ci } else { 29698c2ecf20Sopenharmony_ci res.nh = rt->fib6_nh; 29708c2ecf20Sopenharmony_ci if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, 29718c2ecf20Sopenharmony_ci &ret)) 29728c2ecf20Sopenharmony_ci goto out; 29738c2ecf20Sopenharmony_ci } 29748c2ecf20Sopenharmony_ci } 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci if (!rt) 29778c2ecf20Sopenharmony_ci rt = net->ipv6.fib6_null_entry; 29788c2ecf20Sopenharmony_ci else if (rt->fib6_flags & RTF_REJECT) { 29798c2ecf20Sopenharmony_ci ret = net->ipv6.ip6_null_entry; 29808c2ecf20Sopenharmony_ci goto out; 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) { 29848c2ecf20Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 29858c2ecf20Sopenharmony_ci if (fn) 29868c2ecf20Sopenharmony_ci goto restart; 29878c2ecf20Sopenharmony_ci } 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci res.f6i = rt; 29908c2ecf20Sopenharmony_ci res.nh = rt->fib6_nh; 29918c2ecf20Sopenharmony_ciout: 29928c2ecf20Sopenharmony_ci if (ret) { 29938c2ecf20Sopenharmony_ci ip6_hold_safe(net, &ret); 29948c2ecf20Sopenharmony_ci } else { 29958c2ecf20Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 29968c2ecf20Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 29978c2ecf20Sopenharmony_ci ret = ip6_create_rt_rcu(&res); 29988c2ecf20Sopenharmony_ci } 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci rcu_read_unlock(); 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci trace_fib6_table_lookup(net, &res, table, fl6); 30038c2ecf20Sopenharmony_ci return ret; 30048c2ecf20Sopenharmony_ci}; 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_cistatic struct dst_entry *ip6_route_redirect(struct net *net, 30078c2ecf20Sopenharmony_ci const struct flowi6 *fl6, 30088c2ecf20Sopenharmony_ci const struct sk_buff *skb, 30098c2ecf20Sopenharmony_ci const struct in6_addr *gateway) 30108c2ecf20Sopenharmony_ci{ 30118c2ecf20Sopenharmony_ci int flags = RT6_LOOKUP_F_HAS_SADDR; 30128c2ecf20Sopenharmony_ci struct ip6rd_flowi rdfl; 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci rdfl.fl6 = *fl6; 30158c2ecf20Sopenharmony_ci rdfl.gateway = *gateway; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci return fib6_rule_lookup(net, &rdfl.fl6, skb, 30188c2ecf20Sopenharmony_ci flags, __ip6_route_redirect); 30198c2ecf20Sopenharmony_ci} 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_civoid ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, 30228c2ecf20Sopenharmony_ci kuid_t uid) 30238c2ecf20Sopenharmony_ci{ 30248c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 30258c2ecf20Sopenharmony_ci struct dst_entry *dst; 30268c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 30278c2ecf20Sopenharmony_ci .flowi6_iif = LOOPBACK_IFINDEX, 30288c2ecf20Sopenharmony_ci .flowi6_oif = oif, 30298c2ecf20Sopenharmony_ci .flowi6_mark = mark, 30308c2ecf20Sopenharmony_ci .daddr = iph->daddr, 30318c2ecf20Sopenharmony_ci .saddr = iph->saddr, 30328c2ecf20Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 30338c2ecf20Sopenharmony_ci .flowi6_uid = uid, 30348c2ecf20Sopenharmony_ci }; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr); 30378c2ecf20Sopenharmony_ci rt6_do_redirect(dst, NULL, skb); 30388c2ecf20Sopenharmony_ci dst_release(dst); 30398c2ecf20Sopenharmony_ci} 30408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_redirect); 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_civoid ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif) 30438c2ecf20Sopenharmony_ci{ 30448c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 30458c2ecf20Sopenharmony_ci const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 30468c2ecf20Sopenharmony_ci struct dst_entry *dst; 30478c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 30488c2ecf20Sopenharmony_ci .flowi6_iif = LOOPBACK_IFINDEX, 30498c2ecf20Sopenharmony_ci .flowi6_oif = oif, 30508c2ecf20Sopenharmony_ci .daddr = msg->dest, 30518c2ecf20Sopenharmony_ci .saddr = iph->daddr, 30528c2ecf20Sopenharmony_ci .flowi6_uid = sock_net_uid(net, NULL), 30538c2ecf20Sopenharmony_ci }; 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr); 30568c2ecf20Sopenharmony_ci rt6_do_redirect(dst, NULL, skb); 30578c2ecf20Sopenharmony_ci dst_release(dst); 30588c2ecf20Sopenharmony_ci} 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_civoid ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 30618c2ecf20Sopenharmony_ci{ 30628c2ecf20Sopenharmony_ci ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, 30638c2ecf20Sopenharmony_ci sk->sk_uid); 30648c2ecf20Sopenharmony_ci} 30658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_redirect); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst) 30688c2ecf20Sopenharmony_ci{ 30698c2ecf20Sopenharmony_ci struct net_device *dev = dst->dev; 30708c2ecf20Sopenharmony_ci unsigned int mtu = dst_mtu(dst); 30718c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 30768c2ecf20Sopenharmony_ci mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_ci /* 30798c2ecf20Sopenharmony_ci * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 30808c2ecf20Sopenharmony_ci * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 30818c2ecf20Sopenharmony_ci * IPV6_MAXPLEN is also valid and means: "any MSS, 30828c2ecf20Sopenharmony_ci * rely only on pmtu discovery" 30838c2ecf20Sopenharmony_ci */ 30848c2ecf20Sopenharmony_ci if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 30858c2ecf20Sopenharmony_ci mtu = IPV6_MAXPLEN; 30868c2ecf20Sopenharmony_ci return mtu; 30878c2ecf20Sopenharmony_ci} 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_cistatic unsigned int ip6_mtu(const struct dst_entry *dst) 30908c2ecf20Sopenharmony_ci{ 30918c2ecf20Sopenharmony_ci struct inet6_dev *idev; 30928c2ecf20Sopenharmony_ci unsigned int mtu; 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci mtu = dst_metric_raw(dst, RTAX_MTU); 30958c2ecf20Sopenharmony_ci if (mtu) 30968c2ecf20Sopenharmony_ci goto out; 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci mtu = IPV6_MIN_MTU; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci rcu_read_lock(); 31018c2ecf20Sopenharmony_ci idev = __in6_dev_get(dst->dev); 31028c2ecf20Sopenharmony_ci if (idev) 31038c2ecf20Sopenharmony_ci mtu = idev->cnf.mtu6; 31048c2ecf20Sopenharmony_ci rcu_read_unlock(); 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ciout: 31078c2ecf20Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci return mtu - lwtunnel_headroom(dst->lwtstate, mtu); 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci/* MTU selection: 31138c2ecf20Sopenharmony_ci * 1. mtu on route is locked - use it 31148c2ecf20Sopenharmony_ci * 2. mtu from nexthop exception 31158c2ecf20Sopenharmony_ci * 3. mtu from egress device 31168c2ecf20Sopenharmony_ci * 31178c2ecf20Sopenharmony_ci * based on ip6_dst_mtu_forward and exception logic of 31188c2ecf20Sopenharmony_ci * rt6_find_cached_rt; called with rcu_read_lock 31198c2ecf20Sopenharmony_ci */ 31208c2ecf20Sopenharmony_ciu32 ip6_mtu_from_fib6(const struct fib6_result *res, 31218c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 31228c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 31238c2ecf20Sopenharmony_ci{ 31248c2ecf20Sopenharmony_ci const struct fib6_nh *nh = res->nh; 31258c2ecf20Sopenharmony_ci struct fib6_info *f6i = res->f6i; 31268c2ecf20Sopenharmony_ci struct inet6_dev *idev; 31278c2ecf20Sopenharmony_ci struct rt6_info *rt; 31288c2ecf20Sopenharmony_ci u32 mtu = 0; 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) { 31318c2ecf20Sopenharmony_ci mtu = f6i->fib6_pmtu; 31328c2ecf20Sopenharmony_ci if (mtu) 31338c2ecf20Sopenharmony_ci goto out; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci rt = rt6_find_cached_rt(res, daddr, saddr); 31378c2ecf20Sopenharmony_ci if (unlikely(rt)) { 31388c2ecf20Sopenharmony_ci mtu = dst_metric_raw(&rt->dst, RTAX_MTU); 31398c2ecf20Sopenharmony_ci } else { 31408c2ecf20Sopenharmony_ci struct net_device *dev = nh->fib_nh_dev; 31418c2ecf20Sopenharmony_ci 31428c2ecf20Sopenharmony_ci mtu = IPV6_MIN_MTU; 31438c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 31448c2ecf20Sopenharmony_ci if (idev && idev->cnf.mtu6 > mtu) 31458c2ecf20Sopenharmony_ci mtu = idev->cnf.mtu6; 31468c2ecf20Sopenharmony_ci } 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 31498c2ecf20Sopenharmony_ciout: 31508c2ecf20Sopenharmony_ci return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu); 31518c2ecf20Sopenharmony_ci} 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_cistruct dst_entry *icmp6_dst_alloc(struct net_device *dev, 31548c2ecf20Sopenharmony_ci struct flowi6 *fl6) 31558c2ecf20Sopenharmony_ci{ 31568c2ecf20Sopenharmony_ci struct dst_entry *dst; 31578c2ecf20Sopenharmony_ci struct rt6_info *rt; 31588c2ecf20Sopenharmony_ci struct inet6_dev *idev = in6_dev_get(dev); 31598c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci if (unlikely(!idev)) 31628c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci rt = ip6_dst_alloc(net, dev, 0); 31658c2ecf20Sopenharmony_ci if (unlikely(!rt)) { 31668c2ecf20Sopenharmony_ci in6_dev_put(idev); 31678c2ecf20Sopenharmony_ci dst = ERR_PTR(-ENOMEM); 31688c2ecf20Sopenharmony_ci goto out; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci rt->dst.input = ip6_input; 31728c2ecf20Sopenharmony_ci rt->dst.output = ip6_output; 31738c2ecf20Sopenharmony_ci rt->rt6i_gateway = fl6->daddr; 31748c2ecf20Sopenharmony_ci rt->rt6i_dst.addr = fl6->daddr; 31758c2ecf20Sopenharmony_ci rt->rt6i_dst.plen = 128; 31768c2ecf20Sopenharmony_ci rt->rt6i_idev = idev; 31778c2ecf20Sopenharmony_ci dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci /* Add this dst into uncached_list so that rt6_disable_ip() can 31808c2ecf20Sopenharmony_ci * do proper release of the net_device 31818c2ecf20Sopenharmony_ci */ 31828c2ecf20Sopenharmony_ci rt6_uncached_list_add(rt); 31838c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ciout: 31888c2ecf20Sopenharmony_ci return dst; 31898c2ecf20Sopenharmony_ci} 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops) 31928c2ecf20Sopenharmony_ci{ 31938c2ecf20Sopenharmony_ci struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 31948c2ecf20Sopenharmony_ci int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 31958c2ecf20Sopenharmony_ci int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 31968c2ecf20Sopenharmony_ci int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 31978c2ecf20Sopenharmony_ci unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 31988c2ecf20Sopenharmony_ci unsigned int val; 31998c2ecf20Sopenharmony_ci int entries; 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci entries = dst_entries_get_fast(ops); 32028c2ecf20Sopenharmony_ci if (entries > ops->gc_thresh) 32038c2ecf20Sopenharmony_ci entries = dst_entries_get_slow(ops); 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci if (time_after(rt_last_gc + rt_min_interval, jiffies)) 32068c2ecf20Sopenharmony_ci goto out; 32078c2ecf20Sopenharmony_ci 32088c2ecf20Sopenharmony_ci fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true); 32098c2ecf20Sopenharmony_ci entries = dst_entries_get_slow(ops); 32108c2ecf20Sopenharmony_ci if (entries < ops->gc_thresh) 32118c2ecf20Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, rt_gc_timeout >> 1); 32128c2ecf20Sopenharmony_ciout: 32138c2ecf20Sopenharmony_ci val = atomic_read(&net->ipv6.ip6_rt_gc_expire); 32148c2ecf20Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity)); 32158c2ecf20Sopenharmony_ci} 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_cistatic int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg, 32188c2ecf20Sopenharmony_ci const struct in6_addr *gw_addr, u32 tbid, 32198c2ecf20Sopenharmony_ci int flags, struct fib6_result *res) 32208c2ecf20Sopenharmony_ci{ 32218c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 32228c2ecf20Sopenharmony_ci .flowi6_oif = cfg->fc_ifindex, 32238c2ecf20Sopenharmony_ci .daddr = *gw_addr, 32248c2ecf20Sopenharmony_ci .saddr = cfg->fc_prefsrc, 32258c2ecf20Sopenharmony_ci }; 32268c2ecf20Sopenharmony_ci struct fib6_table *table; 32278c2ecf20Sopenharmony_ci int err; 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci table = fib6_get_table(net, tbid); 32308c2ecf20Sopenharmony_ci if (!table) 32318c2ecf20Sopenharmony_ci return -EINVAL; 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&cfg->fc_prefsrc)) 32348c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags); 32398c2ecf20Sopenharmony_ci if (!err && res->f6i != net->ipv6.fib6_null_entry) 32408c2ecf20Sopenharmony_ci fib6_select_path(net, res, &fl6, cfg->fc_ifindex, 32418c2ecf20Sopenharmony_ci cfg->fc_ifindex != 0, NULL, flags); 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci return err; 32448c2ecf20Sopenharmony_ci} 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_cistatic int ip6_route_check_nh_onlink(struct net *net, 32478c2ecf20Sopenharmony_ci struct fib6_config *cfg, 32488c2ecf20Sopenharmony_ci const struct net_device *dev, 32498c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 32508c2ecf20Sopenharmony_ci{ 32518c2ecf20Sopenharmony_ci u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN; 32528c2ecf20Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 32538c2ecf20Sopenharmony_ci struct fib6_result res = {}; 32548c2ecf20Sopenharmony_ci int err; 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res); 32578c2ecf20Sopenharmony_ci if (!err && !(res.fib6_flags & RTF_REJECT) && 32588c2ecf20Sopenharmony_ci /* ignore match if it is the default route */ 32598c2ecf20Sopenharmony_ci !ipv6_addr_any(&res.f6i->fib6_dst.addr) && 32608c2ecf20Sopenharmony_ci (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) { 32618c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 32628c2ecf20Sopenharmony_ci "Nexthop has invalid gateway or device mismatch"); 32638c2ecf20Sopenharmony_ci err = -EINVAL; 32648c2ecf20Sopenharmony_ci } 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_ci return err; 32678c2ecf20Sopenharmony_ci} 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_cistatic int ip6_route_check_nh(struct net *net, 32708c2ecf20Sopenharmony_ci struct fib6_config *cfg, 32718c2ecf20Sopenharmony_ci struct net_device **_dev, 32728c2ecf20Sopenharmony_ci struct inet6_dev **idev) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 32758c2ecf20Sopenharmony_ci struct net_device *dev = _dev ? *_dev : NULL; 32768c2ecf20Sopenharmony_ci int flags = RT6_LOOKUP_F_IFACE; 32778c2ecf20Sopenharmony_ci struct fib6_result res = {}; 32788c2ecf20Sopenharmony_ci int err = -EHOSTUNREACH; 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci if (cfg->fc_table) { 32818c2ecf20Sopenharmony_ci err = ip6_nh_lookup_table(net, cfg, gw_addr, 32828c2ecf20Sopenharmony_ci cfg->fc_table, flags, &res); 32838c2ecf20Sopenharmony_ci /* gw_addr can not require a gateway or resolve to a reject 32848c2ecf20Sopenharmony_ci * route. If a device is given, it must match the result. 32858c2ecf20Sopenharmony_ci */ 32868c2ecf20Sopenharmony_ci if (err || res.fib6_flags & RTF_REJECT || 32878c2ecf20Sopenharmony_ci res.nh->fib_nh_gw_family || 32888c2ecf20Sopenharmony_ci (dev && dev != res.nh->fib_nh_dev)) 32898c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 32908c2ecf20Sopenharmony_ci } 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci if (err < 0) { 32938c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 32948c2ecf20Sopenharmony_ci .flowi6_oif = cfg->fc_ifindex, 32958c2ecf20Sopenharmony_ci .daddr = *gw_addr, 32968c2ecf20Sopenharmony_ci }; 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags); 32998c2ecf20Sopenharmony_ci if (err || res.fib6_flags & RTF_REJECT || 33008c2ecf20Sopenharmony_ci res.nh->fib_nh_gw_family) 33018c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci if (err) 33048c2ecf20Sopenharmony_ci return err; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci fib6_select_path(net, &res, &fl6, cfg->fc_ifindex, 33078c2ecf20Sopenharmony_ci cfg->fc_ifindex != 0, NULL, flags); 33088c2ecf20Sopenharmony_ci } 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci err = 0; 33118c2ecf20Sopenharmony_ci if (dev) { 33128c2ecf20Sopenharmony_ci if (dev != res.nh->fib_nh_dev) 33138c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 33148c2ecf20Sopenharmony_ci } else { 33158c2ecf20Sopenharmony_ci *_dev = dev = res.nh->fib_nh_dev; 33168c2ecf20Sopenharmony_ci dev_hold(dev); 33178c2ecf20Sopenharmony_ci *idev = in6_dev_get(dev); 33188c2ecf20Sopenharmony_ci } 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci return err; 33218c2ecf20Sopenharmony_ci} 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_cistatic int ip6_validate_gw(struct net *net, struct fib6_config *cfg, 33248c2ecf20Sopenharmony_ci struct net_device **_dev, struct inet6_dev **idev, 33258c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 33268c2ecf20Sopenharmony_ci{ 33278c2ecf20Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 33288c2ecf20Sopenharmony_ci int gwa_type = ipv6_addr_type(gw_addr); 33298c2ecf20Sopenharmony_ci bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true; 33308c2ecf20Sopenharmony_ci const struct net_device *dev = *_dev; 33318c2ecf20Sopenharmony_ci bool need_addr_check = !dev; 33328c2ecf20Sopenharmony_ci int err = -EINVAL; 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci /* if gw_addr is local we will fail to detect this in case 33358c2ecf20Sopenharmony_ci * address is still TENTATIVE (DAD in progress). rt6_lookup() 33368c2ecf20Sopenharmony_ci * will return already-added prefix route via interface that 33378c2ecf20Sopenharmony_ci * prefix route was assigned to, which might be non-loopback. 33388c2ecf20Sopenharmony_ci */ 33398c2ecf20Sopenharmony_ci if (dev && 33408c2ecf20Sopenharmony_ci ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 33418c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 33428c2ecf20Sopenharmony_ci goto out; 33438c2ecf20Sopenharmony_ci } 33448c2ecf20Sopenharmony_ci 33458c2ecf20Sopenharmony_ci if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) { 33468c2ecf20Sopenharmony_ci /* IPv6 strictly inhibits using not link-local 33478c2ecf20Sopenharmony_ci * addresses as nexthop address. 33488c2ecf20Sopenharmony_ci * Otherwise, router will not able to send redirects. 33498c2ecf20Sopenharmony_ci * It is very good, but in some (rare!) circumstances 33508c2ecf20Sopenharmony_ci * (SIT, PtP, NBMA NOARP links) it is handy to allow 33518c2ecf20Sopenharmony_ci * some exceptions. --ANK 33528c2ecf20Sopenharmony_ci * We allow IPv4-mapped nexthops to support RFC4798-type 33538c2ecf20Sopenharmony_ci * addressing 33548c2ecf20Sopenharmony_ci */ 33558c2ecf20Sopenharmony_ci if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) { 33568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid gateway address"); 33578c2ecf20Sopenharmony_ci goto out; 33588c2ecf20Sopenharmony_ci } 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci rcu_read_lock(); 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTNH_F_ONLINK) 33638c2ecf20Sopenharmony_ci err = ip6_route_check_nh_onlink(net, cfg, dev, extack); 33648c2ecf20Sopenharmony_ci else 33658c2ecf20Sopenharmony_ci err = ip6_route_check_nh(net, cfg, _dev, idev); 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci rcu_read_unlock(); 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci if (err) 33708c2ecf20Sopenharmony_ci goto out; 33718c2ecf20Sopenharmony_ci } 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci /* reload in case device was changed */ 33748c2ecf20Sopenharmony_ci dev = *_dev; 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci err = -EINVAL; 33778c2ecf20Sopenharmony_ci if (!dev) { 33788c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Egress device not specified"); 33798c2ecf20Sopenharmony_ci goto out; 33808c2ecf20Sopenharmony_ci } else if (dev->flags & IFF_LOOPBACK) { 33818c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 33828c2ecf20Sopenharmony_ci "Egress device can not be loopback device for this route"); 33838c2ecf20Sopenharmony_ci goto out; 33848c2ecf20Sopenharmony_ci } 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci /* if we did not check gw_addr above, do so now that the 33878c2ecf20Sopenharmony_ci * egress device has been resolved. 33888c2ecf20Sopenharmony_ci */ 33898c2ecf20Sopenharmony_ci if (need_addr_check && 33908c2ecf20Sopenharmony_ci ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 33918c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 33928c2ecf20Sopenharmony_ci goto out; 33938c2ecf20Sopenharmony_ci } 33948c2ecf20Sopenharmony_ci 33958c2ecf20Sopenharmony_ci err = 0; 33968c2ecf20Sopenharmony_ciout: 33978c2ecf20Sopenharmony_ci return err; 33988c2ecf20Sopenharmony_ci} 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_cistatic bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type) 34018c2ecf20Sopenharmony_ci{ 34028c2ecf20Sopenharmony_ci if ((flags & RTF_REJECT) || 34038c2ecf20Sopenharmony_ci (dev && (dev->flags & IFF_LOOPBACK) && 34048c2ecf20Sopenharmony_ci !(addr_type & IPV6_ADDR_LOOPBACK) && 34058c2ecf20Sopenharmony_ci !(flags & (RTF_ANYCAST | RTF_LOCAL)))) 34068c2ecf20Sopenharmony_ci return true; 34078c2ecf20Sopenharmony_ci 34088c2ecf20Sopenharmony_ci return false; 34098c2ecf20Sopenharmony_ci} 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ciint fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh, 34128c2ecf20Sopenharmony_ci struct fib6_config *cfg, gfp_t gfp_flags, 34138c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 34148c2ecf20Sopenharmony_ci{ 34158c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 34168c2ecf20Sopenharmony_ci struct inet6_dev *idev = NULL; 34178c2ecf20Sopenharmony_ci int addr_type; 34188c2ecf20Sopenharmony_ci int err; 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci fib6_nh->fib_nh_family = AF_INET6; 34218c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 34228c2ecf20Sopenharmony_ci fib6_nh->last_probe = jiffies; 34238c2ecf20Sopenharmony_ci#endif 34248c2ecf20Sopenharmony_ci if (cfg->fc_is_fdb) { 34258c2ecf20Sopenharmony_ci fib6_nh->fib_nh_gw6 = cfg->fc_gateway; 34268c2ecf20Sopenharmony_ci fib6_nh->fib_nh_gw_family = AF_INET6; 34278c2ecf20Sopenharmony_ci return 0; 34288c2ecf20Sopenharmony_ci } 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci err = -ENODEV; 34318c2ecf20Sopenharmony_ci if (cfg->fc_ifindex) { 34328c2ecf20Sopenharmony_ci dev = dev_get_by_index(net, cfg->fc_ifindex); 34338c2ecf20Sopenharmony_ci if (!dev) 34348c2ecf20Sopenharmony_ci goto out; 34358c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 34368c2ecf20Sopenharmony_ci if (!idev) 34378c2ecf20Sopenharmony_ci goto out; 34388c2ecf20Sopenharmony_ci } 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTNH_F_ONLINK) { 34418c2ecf20Sopenharmony_ci if (!dev) { 34428c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 34438c2ecf20Sopenharmony_ci "Nexthop device required for onlink"); 34448c2ecf20Sopenharmony_ci goto out; 34458c2ecf20Sopenharmony_ci } 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci if (!(dev->flags & IFF_UP)) { 34488c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 34498c2ecf20Sopenharmony_ci err = -ENETDOWN; 34508c2ecf20Sopenharmony_ci goto out; 34518c2ecf20Sopenharmony_ci } 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci fib6_nh->fib_nh_flags |= RTNH_F_ONLINK; 34548c2ecf20Sopenharmony_ci } 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci fib6_nh->fib_nh_weight = 1; 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci /* We cannot add true routes via loopback here, 34598c2ecf20Sopenharmony_ci * they would result in kernel looping; promote them to reject routes 34608c2ecf20Sopenharmony_ci */ 34618c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&cfg->fc_dst); 34628c2ecf20Sopenharmony_ci if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) { 34638c2ecf20Sopenharmony_ci /* hold loopback dev/idev if we haven't done so. */ 34648c2ecf20Sopenharmony_ci if (dev != net->loopback_dev) { 34658c2ecf20Sopenharmony_ci if (dev) { 34668c2ecf20Sopenharmony_ci dev_put(dev); 34678c2ecf20Sopenharmony_ci in6_dev_put(idev); 34688c2ecf20Sopenharmony_ci } 34698c2ecf20Sopenharmony_ci dev = net->loopback_dev; 34708c2ecf20Sopenharmony_ci dev_hold(dev); 34718c2ecf20Sopenharmony_ci idev = in6_dev_get(dev); 34728c2ecf20Sopenharmony_ci if (!idev) { 34738c2ecf20Sopenharmony_ci err = -ENODEV; 34748c2ecf20Sopenharmony_ci goto out; 34758c2ecf20Sopenharmony_ci } 34768c2ecf20Sopenharmony_ci } 34778c2ecf20Sopenharmony_ci goto pcpu_alloc; 34788c2ecf20Sopenharmony_ci } 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY) { 34818c2ecf20Sopenharmony_ci err = ip6_validate_gw(net, cfg, &dev, &idev, extack); 34828c2ecf20Sopenharmony_ci if (err) 34838c2ecf20Sopenharmony_ci goto out; 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci fib6_nh->fib_nh_gw6 = cfg->fc_gateway; 34868c2ecf20Sopenharmony_ci fib6_nh->fib_nh_gw_family = AF_INET6; 34878c2ecf20Sopenharmony_ci } 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci err = -ENODEV; 34908c2ecf20Sopenharmony_ci if (!dev) 34918c2ecf20Sopenharmony_ci goto out; 34928c2ecf20Sopenharmony_ci 34938c2ecf20Sopenharmony_ci if (!idev || idev->cnf.disable_ipv6) { 34948c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device"); 34958c2ecf20Sopenharmony_ci err = -EACCES; 34968c2ecf20Sopenharmony_ci goto out; 34978c2ecf20Sopenharmony_ci } 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_ci if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) { 35008c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 35018c2ecf20Sopenharmony_ci err = -ENETDOWN; 35028c2ecf20Sopenharmony_ci goto out; 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) && 35068c2ecf20Sopenharmony_ci !netif_carrier_ok(dev)) 35078c2ecf20Sopenharmony_ci fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap, 35108c2ecf20Sopenharmony_ci cfg->fc_encap_type, cfg, gfp_flags, extack); 35118c2ecf20Sopenharmony_ci if (err) 35128c2ecf20Sopenharmony_ci goto out; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_cipcpu_alloc: 35158c2ecf20Sopenharmony_ci fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags); 35168c2ecf20Sopenharmony_ci if (!fib6_nh->rt6i_pcpu) { 35178c2ecf20Sopenharmony_ci err = -ENOMEM; 35188c2ecf20Sopenharmony_ci goto out; 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci fib6_nh->fib_nh_dev = dev; 35228c2ecf20Sopenharmony_ci fib6_nh->fib_nh_oif = dev->ifindex; 35238c2ecf20Sopenharmony_ci err = 0; 35248c2ecf20Sopenharmony_ciout: 35258c2ecf20Sopenharmony_ci if (idev) 35268c2ecf20Sopenharmony_ci in6_dev_put(idev); 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci if (err) { 35298c2ecf20Sopenharmony_ci lwtstate_put(fib6_nh->fib_nh_lws); 35308c2ecf20Sopenharmony_ci fib6_nh->fib_nh_lws = NULL; 35318c2ecf20Sopenharmony_ci if (dev) 35328c2ecf20Sopenharmony_ci dev_put(dev); 35338c2ecf20Sopenharmony_ci } 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci return err; 35368c2ecf20Sopenharmony_ci} 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_civoid fib6_nh_release(struct fib6_nh *fib6_nh) 35398c2ecf20Sopenharmony_ci{ 35408c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci rcu_read_lock(); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci fib6_nh_flush_exceptions(fib6_nh, NULL); 35458c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL); 35468c2ecf20Sopenharmony_ci if (bucket) { 35478c2ecf20Sopenharmony_ci rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL); 35488c2ecf20Sopenharmony_ci kfree(bucket); 35498c2ecf20Sopenharmony_ci } 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci rcu_read_unlock(); 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci if (fib6_nh->rt6i_pcpu) { 35548c2ecf20Sopenharmony_ci int cpu; 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 35578c2ecf20Sopenharmony_ci struct rt6_info **ppcpu_rt; 35588c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt; 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); 35618c2ecf20Sopenharmony_ci pcpu_rt = *ppcpu_rt; 35628c2ecf20Sopenharmony_ci if (pcpu_rt) { 35638c2ecf20Sopenharmony_ci dst_dev_put(&pcpu_rt->dst); 35648c2ecf20Sopenharmony_ci dst_release(&pcpu_rt->dst); 35658c2ecf20Sopenharmony_ci *ppcpu_rt = NULL; 35668c2ecf20Sopenharmony_ci } 35678c2ecf20Sopenharmony_ci } 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci free_percpu(fib6_nh->rt6i_pcpu); 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_ci fib_nh_common_release(&fib6_nh->nh_common); 35738c2ecf20Sopenharmony_ci} 35748c2ecf20Sopenharmony_ci 35758c2ecf20Sopenharmony_civoid fib6_nh_release_dsts(struct fib6_nh *fib6_nh) 35768c2ecf20Sopenharmony_ci{ 35778c2ecf20Sopenharmony_ci int cpu; 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci if (!fib6_nh->rt6i_pcpu) 35808c2ecf20Sopenharmony_ci return; 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 35838c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt, **ppcpu_rt; 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); 35868c2ecf20Sopenharmony_ci pcpu_rt = xchg(ppcpu_rt, NULL); 35878c2ecf20Sopenharmony_ci if (pcpu_rt) { 35888c2ecf20Sopenharmony_ci dst_dev_put(&pcpu_rt->dst); 35898c2ecf20Sopenharmony_ci dst_release(&pcpu_rt->dst); 35908c2ecf20Sopenharmony_ci } 35918c2ecf20Sopenharmony_ci } 35928c2ecf20Sopenharmony_ci} 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_cistatic struct fib6_info *ip6_route_info_create(struct fib6_config *cfg, 35958c2ecf20Sopenharmony_ci gfp_t gfp_flags, 35968c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 35978c2ecf20Sopenharmony_ci{ 35988c2ecf20Sopenharmony_ci struct net *net = cfg->fc_nlinfo.nl_net; 35998c2ecf20Sopenharmony_ci struct fib6_info *rt = NULL; 36008c2ecf20Sopenharmony_ci struct nexthop *nh = NULL; 36018c2ecf20Sopenharmony_ci struct fib6_table *table; 36028c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh; 36038c2ecf20Sopenharmony_ci int err = -EINVAL; 36048c2ecf20Sopenharmony_ci int addr_type; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci /* RTF_PCPU is an internal flag; can not be set by userspace */ 36078c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_PCPU) { 36088c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU"); 36098c2ecf20Sopenharmony_ci goto out; 36108c2ecf20Sopenharmony_ci } 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci /* RTF_CACHE is an internal flag; can not be set by userspace */ 36138c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_CACHE) { 36148c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE"); 36158c2ecf20Sopenharmony_ci goto out; 36168c2ecf20Sopenharmony_ci } 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci if (cfg->fc_type > RTN_MAX) { 36198c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid route type"); 36208c2ecf20Sopenharmony_ci goto out; 36218c2ecf20Sopenharmony_ci } 36228c2ecf20Sopenharmony_ci 36238c2ecf20Sopenharmony_ci if (cfg->fc_dst_len > 128) { 36248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid prefix length"); 36258c2ecf20Sopenharmony_ci goto out; 36268c2ecf20Sopenharmony_ci } 36278c2ecf20Sopenharmony_ci if (cfg->fc_src_len > 128) { 36288c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid source address length"); 36298c2ecf20Sopenharmony_ci goto out; 36308c2ecf20Sopenharmony_ci } 36318c2ecf20Sopenharmony_ci#ifndef CONFIG_IPV6_SUBTREES 36328c2ecf20Sopenharmony_ci if (cfg->fc_src_len) { 36338c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 36348c2ecf20Sopenharmony_ci "Specifying source address requires IPV6_SUBTREES to be enabled"); 36358c2ecf20Sopenharmony_ci goto out; 36368c2ecf20Sopenharmony_ci } 36378c2ecf20Sopenharmony_ci#endif 36388c2ecf20Sopenharmony_ci if (cfg->fc_nh_id) { 36398c2ecf20Sopenharmony_ci nh = nexthop_find_by_id(net, cfg->fc_nh_id); 36408c2ecf20Sopenharmony_ci if (!nh) { 36418c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 36428c2ecf20Sopenharmony_ci goto out; 36438c2ecf20Sopenharmony_ci } 36448c2ecf20Sopenharmony_ci err = fib6_check_nexthop(nh, cfg, extack); 36458c2ecf20Sopenharmony_ci if (err) 36468c2ecf20Sopenharmony_ci goto out; 36478c2ecf20Sopenharmony_ci } 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci err = -ENOBUFS; 36508c2ecf20Sopenharmony_ci if (cfg->fc_nlinfo.nlh && 36518c2ecf20Sopenharmony_ci !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 36528c2ecf20Sopenharmony_ci table = fib6_get_table(net, cfg->fc_table); 36538c2ecf20Sopenharmony_ci if (!table) { 36548c2ecf20Sopenharmony_ci pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 36558c2ecf20Sopenharmony_ci table = fib6_new_table(net, cfg->fc_table); 36568c2ecf20Sopenharmony_ci } 36578c2ecf20Sopenharmony_ci } else { 36588c2ecf20Sopenharmony_ci table = fib6_new_table(net, cfg->fc_table); 36598c2ecf20Sopenharmony_ci } 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci if (!table) 36628c2ecf20Sopenharmony_ci goto out; 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci err = -ENOMEM; 36658c2ecf20Sopenharmony_ci rt = fib6_info_alloc(gfp_flags, !nh); 36668c2ecf20Sopenharmony_ci if (!rt) 36678c2ecf20Sopenharmony_ci goto out; 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len, 36708c2ecf20Sopenharmony_ci extack); 36718c2ecf20Sopenharmony_ci if (IS_ERR(rt->fib6_metrics)) { 36728c2ecf20Sopenharmony_ci err = PTR_ERR(rt->fib6_metrics); 36738c2ecf20Sopenharmony_ci /* Do not leave garbage there. */ 36748c2ecf20Sopenharmony_ci rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics; 36758c2ecf20Sopenharmony_ci goto out_free; 36768c2ecf20Sopenharmony_ci } 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_ADDRCONF) 36798c2ecf20Sopenharmony_ci rt->dst_nocount = true; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_EXPIRES) 36828c2ecf20Sopenharmony_ci fib6_set_expires(rt, jiffies + 36838c2ecf20Sopenharmony_ci clock_t_to_jiffies(cfg->fc_expires)); 36848c2ecf20Sopenharmony_ci else 36858c2ecf20Sopenharmony_ci fib6_clean_expires(rt); 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci if (cfg->fc_protocol == RTPROT_UNSPEC) 36888c2ecf20Sopenharmony_ci cfg->fc_protocol = RTPROT_BOOT; 36898c2ecf20Sopenharmony_ci rt->fib6_protocol = cfg->fc_protocol; 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci rt->fib6_table = table; 36928c2ecf20Sopenharmony_ci rt->fib6_metric = cfg->fc_metric; 36938c2ecf20Sopenharmony_ci rt->fib6_type = cfg->fc_type ? : RTN_UNICAST; 36948c2ecf20Sopenharmony_ci rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY; 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 36978c2ecf20Sopenharmony_ci rt->fib6_dst.plen = cfg->fc_dst_len; 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 37008c2ecf20Sopenharmony_ci ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len); 37018c2ecf20Sopenharmony_ci rt->fib6_src.plen = cfg->fc_src_len; 37028c2ecf20Sopenharmony_ci#endif 37038c2ecf20Sopenharmony_ci if (nh) { 37048c2ecf20Sopenharmony_ci if (rt->fib6_src.plen) { 37058c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing"); 37068c2ecf20Sopenharmony_ci goto out_free; 37078c2ecf20Sopenharmony_ci } 37088c2ecf20Sopenharmony_ci if (!nexthop_get(nh)) { 37098c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop has been deleted"); 37108c2ecf20Sopenharmony_ci goto out_free; 37118c2ecf20Sopenharmony_ci } 37128c2ecf20Sopenharmony_ci rt->nh = nh; 37138c2ecf20Sopenharmony_ci fib6_nh = nexthop_fib6_nh(rt->nh); 37148c2ecf20Sopenharmony_ci } else { 37158c2ecf20Sopenharmony_ci err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack); 37168c2ecf20Sopenharmony_ci if (err) 37178c2ecf20Sopenharmony_ci goto out; 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci fib6_nh = rt->fib6_nh; 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci /* We cannot add true routes via loopback here, they would 37228c2ecf20Sopenharmony_ci * result in kernel looping; promote them to reject routes 37238c2ecf20Sopenharmony_ci */ 37248c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&cfg->fc_dst); 37258c2ecf20Sopenharmony_ci if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev, 37268c2ecf20Sopenharmony_ci addr_type)) 37278c2ecf20Sopenharmony_ci rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP; 37288c2ecf20Sopenharmony_ci } 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 37318c2ecf20Sopenharmony_ci struct net_device *dev = fib6_nh->fib_nh_dev; 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 37348c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid source address"); 37358c2ecf20Sopenharmony_ci err = -EINVAL; 37368c2ecf20Sopenharmony_ci goto out; 37378c2ecf20Sopenharmony_ci } 37388c2ecf20Sopenharmony_ci rt->fib6_prefsrc.addr = cfg->fc_prefsrc; 37398c2ecf20Sopenharmony_ci rt->fib6_prefsrc.plen = 128; 37408c2ecf20Sopenharmony_ci } else 37418c2ecf20Sopenharmony_ci rt->fib6_prefsrc.plen = 0; 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci return rt; 37448c2ecf20Sopenharmony_ciout: 37458c2ecf20Sopenharmony_ci fib6_info_release(rt); 37468c2ecf20Sopenharmony_ci return ERR_PTR(err); 37478c2ecf20Sopenharmony_ciout_free: 37488c2ecf20Sopenharmony_ci ip_fib_metrics_put(rt->fib6_metrics); 37498c2ecf20Sopenharmony_ci kfree(rt); 37508c2ecf20Sopenharmony_ci return ERR_PTR(err); 37518c2ecf20Sopenharmony_ci} 37528c2ecf20Sopenharmony_ci 37538c2ecf20Sopenharmony_ciint ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags, 37548c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 37558c2ecf20Sopenharmony_ci{ 37568c2ecf20Sopenharmony_ci struct fib6_info *rt; 37578c2ecf20Sopenharmony_ci int err; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci rt = ip6_route_info_create(cfg, gfp_flags, extack); 37608c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 37618c2ecf20Sopenharmony_ci return PTR_ERR(rt); 37628c2ecf20Sopenharmony_ci 37638c2ecf20Sopenharmony_ci err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); 37648c2ecf20Sopenharmony_ci fib6_info_release(rt); 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci return err; 37678c2ecf20Sopenharmony_ci} 37688c2ecf20Sopenharmony_ci 37698c2ecf20Sopenharmony_cistatic int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info) 37708c2ecf20Sopenharmony_ci{ 37718c2ecf20Sopenharmony_ci struct net *net = info->nl_net; 37728c2ecf20Sopenharmony_ci struct fib6_table *table; 37738c2ecf20Sopenharmony_ci int err; 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) { 37768c2ecf20Sopenharmony_ci err = -ENOENT; 37778c2ecf20Sopenharmony_ci goto out; 37788c2ecf20Sopenharmony_ci } 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_ci table = rt->fib6_table; 37818c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 37828c2ecf20Sopenharmony_ci err = fib6_del(rt, info); 37838c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ciout: 37868c2ecf20Sopenharmony_ci fib6_info_release(rt); 37878c2ecf20Sopenharmony_ci return err; 37888c2ecf20Sopenharmony_ci} 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ciint ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify) 37918c2ecf20Sopenharmony_ci{ 37928c2ecf20Sopenharmony_ci struct nl_info info = { 37938c2ecf20Sopenharmony_ci .nl_net = net, 37948c2ecf20Sopenharmony_ci .skip_notify = skip_notify 37958c2ecf20Sopenharmony_ci }; 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci return __ip6_del_rt(rt, &info); 37988c2ecf20Sopenharmony_ci} 37998c2ecf20Sopenharmony_ci 38008c2ecf20Sopenharmony_cistatic int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) 38018c2ecf20Sopenharmony_ci{ 38028c2ecf20Sopenharmony_ci struct nl_info *info = &cfg->fc_nlinfo; 38038c2ecf20Sopenharmony_ci struct net *net = info->nl_net; 38048c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 38058c2ecf20Sopenharmony_ci struct fib6_table *table; 38068c2ecf20Sopenharmony_ci int err = -ENOENT; 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) 38098c2ecf20Sopenharmony_ci goto out_put; 38108c2ecf20Sopenharmony_ci table = rt->fib6_table; 38118c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { 38148c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 38158c2ecf20Sopenharmony_ci struct fib6_node *fn; 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_ci /* prefer to send a single notification with all hops */ 38188c2ecf20Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 38198c2ecf20Sopenharmony_ci if (skb) { 38208c2ecf20Sopenharmony_ci u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ci if (rt6_fill_node(net, skb, rt, NULL, 38238c2ecf20Sopenharmony_ci NULL, NULL, 0, RTM_DELROUTE, 38248c2ecf20Sopenharmony_ci info->portid, seq, 0) < 0) { 38258c2ecf20Sopenharmony_ci kfree_skb(skb); 38268c2ecf20Sopenharmony_ci skb = NULL; 38278c2ecf20Sopenharmony_ci } else 38288c2ecf20Sopenharmony_ci info->skip_notify = 1; 38298c2ecf20Sopenharmony_ci } 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci /* 'rt' points to the first sibling route. If it is not the 38328c2ecf20Sopenharmony_ci * leaf, then we do not need to send a notification. Otherwise, 38338c2ecf20Sopenharmony_ci * we need to check if the last sibling has a next route or not 38348c2ecf20Sopenharmony_ci * and emit a replace or delete notification, respectively. 38358c2ecf20Sopenharmony_ci */ 38368c2ecf20Sopenharmony_ci info->skip_notify_kernel = 1; 38378c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(rt->fib6_node, 38388c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 38398c2ecf20Sopenharmony_ci if (rcu_access_pointer(fn->leaf) == rt) { 38408c2ecf20Sopenharmony_ci struct fib6_info *last_sibling, *replace_rt; 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_ci last_sibling = list_last_entry(&rt->fib6_siblings, 38438c2ecf20Sopenharmony_ci struct fib6_info, 38448c2ecf20Sopenharmony_ci fib6_siblings); 38458c2ecf20Sopenharmony_ci replace_rt = rcu_dereference_protected( 38468c2ecf20Sopenharmony_ci last_sibling->fib6_next, 38478c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 38488c2ecf20Sopenharmony_ci if (replace_rt) 38498c2ecf20Sopenharmony_ci call_fib6_entry_notifiers_replace(net, 38508c2ecf20Sopenharmony_ci replace_rt); 38518c2ecf20Sopenharmony_ci else 38528c2ecf20Sopenharmony_ci call_fib6_multipath_entry_notifiers(net, 38538c2ecf20Sopenharmony_ci FIB_EVENT_ENTRY_DEL, 38548c2ecf20Sopenharmony_ci rt, rt->fib6_nsiblings, 38558c2ecf20Sopenharmony_ci NULL); 38568c2ecf20Sopenharmony_ci } 38578c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 38588c2ecf20Sopenharmony_ci &rt->fib6_siblings, 38598c2ecf20Sopenharmony_ci fib6_siblings) { 38608c2ecf20Sopenharmony_ci err = fib6_del(sibling, info); 38618c2ecf20Sopenharmony_ci if (err) 38628c2ecf20Sopenharmony_ci goto out_unlock; 38638c2ecf20Sopenharmony_ci } 38648c2ecf20Sopenharmony_ci } 38658c2ecf20Sopenharmony_ci 38668c2ecf20Sopenharmony_ci err = fib6_del(rt, info); 38678c2ecf20Sopenharmony_ciout_unlock: 38688c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 38698c2ecf20Sopenharmony_ciout_put: 38708c2ecf20Sopenharmony_ci fib6_info_release(rt); 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_ci if (skb) { 38738c2ecf20Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 38748c2ecf20Sopenharmony_ci info->nlh, gfp_any()); 38758c2ecf20Sopenharmony_ci } 38768c2ecf20Sopenharmony_ci return err; 38778c2ecf20Sopenharmony_ci} 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_cistatic int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg) 38808c2ecf20Sopenharmony_ci{ 38818c2ecf20Sopenharmony_ci int rc = -ESRCH; 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex) 38848c2ecf20Sopenharmony_ci goto out; 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY && 38878c2ecf20Sopenharmony_ci !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 38888c2ecf20Sopenharmony_ci goto out; 38898c2ecf20Sopenharmony_ci 38908c2ecf20Sopenharmony_ci rc = rt6_remove_exception_rt(rt); 38918c2ecf20Sopenharmony_ciout: 38928c2ecf20Sopenharmony_ci return rc; 38938c2ecf20Sopenharmony_ci} 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_cistatic int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt, 38968c2ecf20Sopenharmony_ci struct fib6_nh *nh) 38978c2ecf20Sopenharmony_ci{ 38988c2ecf20Sopenharmony_ci struct fib6_result res = { 38998c2ecf20Sopenharmony_ci .f6i = rt, 39008c2ecf20Sopenharmony_ci .nh = nh, 39018c2ecf20Sopenharmony_ci }; 39028c2ecf20Sopenharmony_ci struct rt6_info *rt_cache; 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src); 39058c2ecf20Sopenharmony_ci if (rt_cache) 39068c2ecf20Sopenharmony_ci return __ip6_del_cached_rt(rt_cache, cfg); 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_ci return 0; 39098c2ecf20Sopenharmony_ci} 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_cistruct fib6_nh_del_cached_rt_arg { 39128c2ecf20Sopenharmony_ci struct fib6_config *cfg; 39138c2ecf20Sopenharmony_ci struct fib6_info *f6i; 39148c2ecf20Sopenharmony_ci}; 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_cistatic int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg) 39178c2ecf20Sopenharmony_ci{ 39188c2ecf20Sopenharmony_ci struct fib6_nh_del_cached_rt_arg *arg = _arg; 39198c2ecf20Sopenharmony_ci int rc; 39208c2ecf20Sopenharmony_ci 39218c2ecf20Sopenharmony_ci rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh); 39228c2ecf20Sopenharmony_ci return rc != -ESRCH ? rc : 0; 39238c2ecf20Sopenharmony_ci} 39248c2ecf20Sopenharmony_ci 39258c2ecf20Sopenharmony_cistatic int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i) 39268c2ecf20Sopenharmony_ci{ 39278c2ecf20Sopenharmony_ci struct fib6_nh_del_cached_rt_arg arg = { 39288c2ecf20Sopenharmony_ci .cfg = cfg, 39298c2ecf20Sopenharmony_ci .f6i = f6i 39308c2ecf20Sopenharmony_ci }; 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ci return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg); 39338c2ecf20Sopenharmony_ci} 39348c2ecf20Sopenharmony_ci 39358c2ecf20Sopenharmony_cistatic int ip6_route_del(struct fib6_config *cfg, 39368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 39378c2ecf20Sopenharmony_ci{ 39388c2ecf20Sopenharmony_ci struct fib6_table *table; 39398c2ecf20Sopenharmony_ci struct fib6_info *rt; 39408c2ecf20Sopenharmony_ci struct fib6_node *fn; 39418c2ecf20Sopenharmony_ci int err = -ESRCH; 39428c2ecf20Sopenharmony_ci 39438c2ecf20Sopenharmony_ci table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 39448c2ecf20Sopenharmony_ci if (!table) { 39458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "FIB table does not exist"); 39468c2ecf20Sopenharmony_ci return err; 39478c2ecf20Sopenharmony_ci } 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci rcu_read_lock(); 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci fn = fib6_locate(&table->tb6_root, 39528c2ecf20Sopenharmony_ci &cfg->fc_dst, cfg->fc_dst_len, 39538c2ecf20Sopenharmony_ci &cfg->fc_src, cfg->fc_src_len, 39548c2ecf20Sopenharmony_ci !(cfg->fc_flags & RTF_CACHE)); 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci if (fn) { 39578c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 39588c2ecf20Sopenharmony_ci struct fib6_nh *nh; 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci if (rt->nh && cfg->fc_nh_id && 39618c2ecf20Sopenharmony_ci rt->nh->id != cfg->fc_nh_id) 39628c2ecf20Sopenharmony_ci continue; 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_CACHE) { 39658c2ecf20Sopenharmony_ci int rc = 0; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (rt->nh) { 39688c2ecf20Sopenharmony_ci rc = ip6_del_cached_rt_nh(cfg, rt); 39698c2ecf20Sopenharmony_ci } else if (cfg->fc_nh_id) { 39708c2ecf20Sopenharmony_ci continue; 39718c2ecf20Sopenharmony_ci } else { 39728c2ecf20Sopenharmony_ci nh = rt->fib6_nh; 39738c2ecf20Sopenharmony_ci rc = ip6_del_cached_rt(cfg, rt, nh); 39748c2ecf20Sopenharmony_ci } 39758c2ecf20Sopenharmony_ci if (rc != -ESRCH) { 39768c2ecf20Sopenharmony_ci rcu_read_unlock(); 39778c2ecf20Sopenharmony_ci return rc; 39788c2ecf20Sopenharmony_ci } 39798c2ecf20Sopenharmony_ci continue; 39808c2ecf20Sopenharmony_ci } 39818c2ecf20Sopenharmony_ci 39828c2ecf20Sopenharmony_ci if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric) 39838c2ecf20Sopenharmony_ci continue; 39848c2ecf20Sopenharmony_ci if (cfg->fc_protocol && 39858c2ecf20Sopenharmony_ci cfg->fc_protocol != rt->fib6_protocol) 39868c2ecf20Sopenharmony_ci continue; 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci if (rt->nh) { 39898c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 39908c2ecf20Sopenharmony_ci continue; 39918c2ecf20Sopenharmony_ci rcu_read_unlock(); 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_ci return __ip6_del_rt(rt, &cfg->fc_nlinfo); 39948c2ecf20Sopenharmony_ci } 39958c2ecf20Sopenharmony_ci if (cfg->fc_nh_id) 39968c2ecf20Sopenharmony_ci continue; 39978c2ecf20Sopenharmony_ci 39988c2ecf20Sopenharmony_ci nh = rt->fib6_nh; 39998c2ecf20Sopenharmony_ci if (cfg->fc_ifindex && 40008c2ecf20Sopenharmony_ci (!nh->fib_nh_dev || 40018c2ecf20Sopenharmony_ci nh->fib_nh_dev->ifindex != cfg->fc_ifindex)) 40028c2ecf20Sopenharmony_ci continue; 40038c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY && 40048c2ecf20Sopenharmony_ci !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6)) 40058c2ecf20Sopenharmony_ci continue; 40068c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 40078c2ecf20Sopenharmony_ci continue; 40088c2ecf20Sopenharmony_ci rcu_read_unlock(); 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci /* if gateway was specified only delete the one hop */ 40118c2ecf20Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY) 40128c2ecf20Sopenharmony_ci return __ip6_del_rt(rt, &cfg->fc_nlinfo); 40138c2ecf20Sopenharmony_ci 40148c2ecf20Sopenharmony_ci return __ip6_del_rt_siblings(rt, cfg); 40158c2ecf20Sopenharmony_ci } 40168c2ecf20Sopenharmony_ci } 40178c2ecf20Sopenharmony_ci rcu_read_unlock(); 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci return err; 40208c2ecf20Sopenharmony_ci} 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 40238c2ecf20Sopenharmony_ci{ 40248c2ecf20Sopenharmony_ci struct netevent_redirect netevent; 40258c2ecf20Sopenharmony_ci struct rt6_info *rt, *nrt = NULL; 40268c2ecf20Sopenharmony_ci struct fib6_result res = {}; 40278c2ecf20Sopenharmony_ci struct ndisc_options ndopts; 40288c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 40298c2ecf20Sopenharmony_ci struct neighbour *neigh; 40308c2ecf20Sopenharmony_ci struct rd_msg *msg; 40318c2ecf20Sopenharmony_ci int optlen, on_link; 40328c2ecf20Sopenharmony_ci u8 *lladdr; 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_ci optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 40358c2ecf20Sopenharmony_ci optlen -= sizeof(*msg); 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci if (optlen < 0) { 40388c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 40398c2ecf20Sopenharmony_ci return; 40408c2ecf20Sopenharmony_ci } 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_ci msg = (struct rd_msg *)icmp6_hdr(skb); 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->dest)) { 40458c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 40468c2ecf20Sopenharmony_ci return; 40478c2ecf20Sopenharmony_ci } 40488c2ecf20Sopenharmony_ci 40498c2ecf20Sopenharmony_ci on_link = 0; 40508c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&msg->dest, &msg->target)) { 40518c2ecf20Sopenharmony_ci on_link = 1; 40528c2ecf20Sopenharmony_ci } else if (ipv6_addr_type(&msg->target) != 40538c2ecf20Sopenharmony_ci (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 40548c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 40558c2ecf20Sopenharmony_ci return; 40568c2ecf20Sopenharmony_ci } 40578c2ecf20Sopenharmony_ci 40588c2ecf20Sopenharmony_ci in6_dev = __in6_dev_get(skb->dev); 40598c2ecf20Sopenharmony_ci if (!in6_dev) 40608c2ecf20Sopenharmony_ci return; 40618c2ecf20Sopenharmony_ci if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 40628c2ecf20Sopenharmony_ci return; 40638c2ecf20Sopenharmony_ci 40648c2ecf20Sopenharmony_ci /* RFC2461 8.1: 40658c2ecf20Sopenharmony_ci * The IP source address of the Redirect MUST be the same as the current 40668c2ecf20Sopenharmony_ci * first-hop router for the specified ICMP Destination Address. 40678c2ecf20Sopenharmony_ci */ 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { 40708c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 40718c2ecf20Sopenharmony_ci return; 40728c2ecf20Sopenharmony_ci } 40738c2ecf20Sopenharmony_ci 40748c2ecf20Sopenharmony_ci lladdr = NULL; 40758c2ecf20Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr) { 40768c2ecf20Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 40778c2ecf20Sopenharmony_ci skb->dev); 40788c2ecf20Sopenharmony_ci if (!lladdr) { 40798c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 40808c2ecf20Sopenharmony_ci return; 40818c2ecf20Sopenharmony_ci } 40828c2ecf20Sopenharmony_ci } 40838c2ecf20Sopenharmony_ci 40848c2ecf20Sopenharmony_ci rt = (struct rt6_info *) dst; 40858c2ecf20Sopenharmony_ci if (rt->rt6i_flags & RTF_REJECT) { 40868c2ecf20Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 40878c2ecf20Sopenharmony_ci return; 40888c2ecf20Sopenharmony_ci } 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci /* Redirect received -> path was valid. 40918c2ecf20Sopenharmony_ci * Look, redirects are sent only in response to data packets, 40928c2ecf20Sopenharmony_ci * so that this nexthop apparently is reachable. --ANK 40938c2ecf20Sopenharmony_ci */ 40948c2ecf20Sopenharmony_ci dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr); 40958c2ecf20Sopenharmony_ci 40968c2ecf20Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 40978c2ecf20Sopenharmony_ci if (!neigh) 40988c2ecf20Sopenharmony_ci return; 40998c2ecf20Sopenharmony_ci 41008c2ecf20Sopenharmony_ci /* 41018c2ecf20Sopenharmony_ci * We have finally decided to accept it. 41028c2ecf20Sopenharmony_ci */ 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 41058c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 41068c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 41078c2ecf20Sopenharmony_ci (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 41088c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_ISROUTER)), 41098c2ecf20Sopenharmony_ci NDISC_REDIRECT, &ndopts); 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ci rcu_read_lock(); 41128c2ecf20Sopenharmony_ci res.f6i = rcu_dereference(rt->from); 41138c2ecf20Sopenharmony_ci if (!res.f6i) 41148c2ecf20Sopenharmony_ci goto out; 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_ci if (res.f6i->nh) { 41178c2ecf20Sopenharmony_ci struct fib6_nh_match_arg arg = { 41188c2ecf20Sopenharmony_ci .dev = dst->dev, 41198c2ecf20Sopenharmony_ci .gw = &rt->rt6i_gateway, 41208c2ecf20Sopenharmony_ci }; 41218c2ecf20Sopenharmony_ci 41228c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(res.f6i->nh, 41238c2ecf20Sopenharmony_ci fib6_nh_find_match, &arg); 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci /* fib6_info uses a nexthop that does not have fib6_nh 41268c2ecf20Sopenharmony_ci * using the dst->dev. Should be impossible 41278c2ecf20Sopenharmony_ci */ 41288c2ecf20Sopenharmony_ci if (!arg.match) 41298c2ecf20Sopenharmony_ci goto out; 41308c2ecf20Sopenharmony_ci res.nh = arg.match; 41318c2ecf20Sopenharmony_ci } else { 41328c2ecf20Sopenharmony_ci res.nh = res.f6i->fib6_nh; 41338c2ecf20Sopenharmony_ci } 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 41368c2ecf20Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 41378c2ecf20Sopenharmony_ci nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL); 41388c2ecf20Sopenharmony_ci if (!nrt) 41398c2ecf20Sopenharmony_ci goto out; 41408c2ecf20Sopenharmony_ci 41418c2ecf20Sopenharmony_ci nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 41428c2ecf20Sopenharmony_ci if (on_link) 41438c2ecf20Sopenharmony_ci nrt->rt6i_flags &= ~RTF_GATEWAY; 41448c2ecf20Sopenharmony_ci 41458c2ecf20Sopenharmony_ci nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 41468c2ecf20Sopenharmony_ci 41478c2ecf20Sopenharmony_ci /* rt6_insert_exception() will take care of duplicated exceptions */ 41488c2ecf20Sopenharmony_ci if (rt6_insert_exception(nrt, &res)) { 41498c2ecf20Sopenharmony_ci dst_release_immediate(&nrt->dst); 41508c2ecf20Sopenharmony_ci goto out; 41518c2ecf20Sopenharmony_ci } 41528c2ecf20Sopenharmony_ci 41538c2ecf20Sopenharmony_ci netevent.old = &rt->dst; 41548c2ecf20Sopenharmony_ci netevent.new = &nrt->dst; 41558c2ecf20Sopenharmony_ci netevent.daddr = &msg->dest; 41568c2ecf20Sopenharmony_ci netevent.neigh = neigh; 41578c2ecf20Sopenharmony_ci call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ciout: 41608c2ecf20Sopenharmony_ci rcu_read_unlock(); 41618c2ecf20Sopenharmony_ci neigh_release(neigh); 41628c2ecf20Sopenharmony_ci} 41638c2ecf20Sopenharmony_ci 41648c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 41658c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net, 41668c2ecf20Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 41678c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr, 41688c2ecf20Sopenharmony_ci struct net_device *dev) 41698c2ecf20Sopenharmony_ci{ 41708c2ecf20Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 41718c2ecf20Sopenharmony_ci int ifindex = dev->ifindex; 41728c2ecf20Sopenharmony_ci struct fib6_node *fn; 41738c2ecf20Sopenharmony_ci struct fib6_info *rt = NULL; 41748c2ecf20Sopenharmony_ci struct fib6_table *table; 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ci table = fib6_get_table(net, tb_id); 41778c2ecf20Sopenharmony_ci if (!table) 41788c2ecf20Sopenharmony_ci return NULL; 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_ci rcu_read_lock(); 41818c2ecf20Sopenharmony_ci fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); 41828c2ecf20Sopenharmony_ci if (!fn) 41838c2ecf20Sopenharmony_ci goto out; 41848c2ecf20Sopenharmony_ci 41858c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 41868c2ecf20Sopenharmony_ci /* these routes do not use nexthops */ 41878c2ecf20Sopenharmony_ci if (rt->nh) 41888c2ecf20Sopenharmony_ci continue; 41898c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex) 41908c2ecf20Sopenharmony_ci continue; 41918c2ecf20Sopenharmony_ci if (!(rt->fib6_flags & RTF_ROUTEINFO) || 41928c2ecf20Sopenharmony_ci !rt->fib6_nh->fib_nh_gw_family) 41938c2ecf20Sopenharmony_ci continue; 41948c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr)) 41958c2ecf20Sopenharmony_ci continue; 41968c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 41978c2ecf20Sopenharmony_ci continue; 41988c2ecf20Sopenharmony_ci break; 41998c2ecf20Sopenharmony_ci } 42008c2ecf20Sopenharmony_ciout: 42018c2ecf20Sopenharmony_ci rcu_read_unlock(); 42028c2ecf20Sopenharmony_ci return rt; 42038c2ecf20Sopenharmony_ci} 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net, 42068c2ecf20Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 42078c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr, 42088c2ecf20Sopenharmony_ci struct net_device *dev, 42098c2ecf20Sopenharmony_ci unsigned int pref) 42108c2ecf20Sopenharmony_ci{ 42118c2ecf20Sopenharmony_ci struct fib6_config cfg = { 42128c2ecf20Sopenharmony_ci .fc_metric = IP6_RT_PRIO_USER, 42138c2ecf20Sopenharmony_ci .fc_ifindex = dev->ifindex, 42148c2ecf20Sopenharmony_ci .fc_dst_len = prefixlen, 42158c2ecf20Sopenharmony_ci .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 42168c2ecf20Sopenharmony_ci RTF_UP | RTF_PREF(pref), 42178c2ecf20Sopenharmony_ci .fc_protocol = RTPROT_RA, 42188c2ecf20Sopenharmony_ci .fc_type = RTN_UNICAST, 42198c2ecf20Sopenharmony_ci .fc_nlinfo.portid = 0, 42208c2ecf20Sopenharmony_ci .fc_nlinfo.nlh = NULL, 42218c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = net, 42228c2ecf20Sopenharmony_ci }; 42238c2ecf20Sopenharmony_ci 42248c2ecf20Sopenharmony_ci cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 42258c2ecf20Sopenharmony_ci cfg.fc_dst = *prefix; 42268c2ecf20Sopenharmony_ci cfg.fc_gateway = *gwaddr; 42278c2ecf20Sopenharmony_ci 42288c2ecf20Sopenharmony_ci /* We should treat it as a default route if prefix length is 0. */ 42298c2ecf20Sopenharmony_ci if (!prefixlen) 42308c2ecf20Sopenharmony_ci cfg.fc_flags |= RTF_DEFAULT; 42318c2ecf20Sopenharmony_ci 42328c2ecf20Sopenharmony_ci ip6_route_add(&cfg, GFP_ATOMIC, NULL); 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); 42358c2ecf20Sopenharmony_ci} 42368c2ecf20Sopenharmony_ci#endif 42378c2ecf20Sopenharmony_ci 42388c2ecf20Sopenharmony_cistruct fib6_info *rt6_get_dflt_router(struct net *net, 42398c2ecf20Sopenharmony_ci const struct in6_addr *addr, 42408c2ecf20Sopenharmony_ci struct net_device *dev) 42418c2ecf20Sopenharmony_ci{ 42428c2ecf20Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; 42438c2ecf20Sopenharmony_ci struct fib6_info *rt; 42448c2ecf20Sopenharmony_ci struct fib6_table *table; 42458c2ecf20Sopenharmony_ci 42468c2ecf20Sopenharmony_ci table = fib6_get_table(net, tb_id); 42478c2ecf20Sopenharmony_ci if (!table) 42488c2ecf20Sopenharmony_ci return NULL; 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci rcu_read_lock(); 42518c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(&table->tb6_root) { 42528c2ecf20Sopenharmony_ci struct fib6_nh *nh; 42538c2ecf20Sopenharmony_ci 42548c2ecf20Sopenharmony_ci /* RA routes do not use nexthops */ 42558c2ecf20Sopenharmony_ci if (rt->nh) 42568c2ecf20Sopenharmony_ci continue; 42578c2ecf20Sopenharmony_ci 42588c2ecf20Sopenharmony_ci nh = rt->fib6_nh; 42598c2ecf20Sopenharmony_ci if (dev == nh->fib_nh_dev && 42608c2ecf20Sopenharmony_ci ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 42618c2ecf20Sopenharmony_ci ipv6_addr_equal(&nh->fib_nh_gw6, addr)) 42628c2ecf20Sopenharmony_ci break; 42638c2ecf20Sopenharmony_ci } 42648c2ecf20Sopenharmony_ci if (rt && !fib6_info_hold_safe(rt)) 42658c2ecf20Sopenharmony_ci rt = NULL; 42668c2ecf20Sopenharmony_ci rcu_read_unlock(); 42678c2ecf20Sopenharmony_ci return rt; 42688c2ecf20Sopenharmony_ci} 42698c2ecf20Sopenharmony_ci 42708c2ecf20Sopenharmony_cistruct fib6_info *rt6_add_dflt_router(struct net *net, 42718c2ecf20Sopenharmony_ci const struct in6_addr *gwaddr, 42728c2ecf20Sopenharmony_ci struct net_device *dev, 42738c2ecf20Sopenharmony_ci unsigned int pref) 42748c2ecf20Sopenharmony_ci{ 42758c2ecf20Sopenharmony_ci struct fib6_config cfg = { 42768c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 42778c2ecf20Sopenharmony_ci .fc_metric = IP6_RT_PRIO_USER, 42788c2ecf20Sopenharmony_ci .fc_ifindex = dev->ifindex, 42798c2ecf20Sopenharmony_ci .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 42808c2ecf20Sopenharmony_ci RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 42818c2ecf20Sopenharmony_ci .fc_protocol = RTPROT_RA, 42828c2ecf20Sopenharmony_ci .fc_type = RTN_UNICAST, 42838c2ecf20Sopenharmony_ci .fc_nlinfo.portid = 0, 42848c2ecf20Sopenharmony_ci .fc_nlinfo.nlh = NULL, 42858c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = net, 42868c2ecf20Sopenharmony_ci }; 42878c2ecf20Sopenharmony_ci 42888c2ecf20Sopenharmony_ci cfg.fc_gateway = *gwaddr; 42898c2ecf20Sopenharmony_ci 42908c2ecf20Sopenharmony_ci if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) { 42918c2ecf20Sopenharmony_ci struct fib6_table *table; 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci table = fib6_get_table(dev_net(dev), cfg.fc_table); 42948c2ecf20Sopenharmony_ci if (table) 42958c2ecf20Sopenharmony_ci table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; 42968c2ecf20Sopenharmony_ci } 42978c2ecf20Sopenharmony_ci 42988c2ecf20Sopenharmony_ci return rt6_get_dflt_router(net, gwaddr, dev); 42998c2ecf20Sopenharmony_ci} 43008c2ecf20Sopenharmony_ci 43018c2ecf20Sopenharmony_cistatic void __rt6_purge_dflt_routers(struct net *net, 43028c2ecf20Sopenharmony_ci struct fib6_table *table) 43038c2ecf20Sopenharmony_ci{ 43048c2ecf20Sopenharmony_ci struct fib6_info *rt; 43058c2ecf20Sopenharmony_ci 43068c2ecf20Sopenharmony_cirestart: 43078c2ecf20Sopenharmony_ci rcu_read_lock(); 43088c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(&table->tb6_root) { 43098c2ecf20Sopenharmony_ci struct net_device *dev = fib6_info_nh_dev(rt); 43108c2ecf20Sopenharmony_ci struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; 43118c2ecf20Sopenharmony_ci 43128c2ecf20Sopenharmony_ci if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 43138c2ecf20Sopenharmony_ci (!idev || idev->cnf.accept_ra != 2) && 43148c2ecf20Sopenharmony_ci fib6_info_hold_safe(rt)) { 43158c2ecf20Sopenharmony_ci rcu_read_unlock(); 43168c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 43178c2ecf20Sopenharmony_ci goto restart; 43188c2ecf20Sopenharmony_ci } 43198c2ecf20Sopenharmony_ci } 43208c2ecf20Sopenharmony_ci rcu_read_unlock(); 43218c2ecf20Sopenharmony_ci 43228c2ecf20Sopenharmony_ci table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; 43238c2ecf20Sopenharmony_ci} 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_civoid rt6_purge_dflt_routers(struct net *net) 43268c2ecf20Sopenharmony_ci{ 43278c2ecf20Sopenharmony_ci struct fib6_table *table; 43288c2ecf20Sopenharmony_ci struct hlist_head *head; 43298c2ecf20Sopenharmony_ci unsigned int h; 43308c2ecf20Sopenharmony_ci 43318c2ecf20Sopenharmony_ci rcu_read_lock(); 43328c2ecf20Sopenharmony_ci 43338c2ecf20Sopenharmony_ci for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 43348c2ecf20Sopenharmony_ci head = &net->ipv6.fib_table_hash[h]; 43358c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(table, head, tb6_hlist) { 43368c2ecf20Sopenharmony_ci if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) 43378c2ecf20Sopenharmony_ci __rt6_purge_dflt_routers(net, table); 43388c2ecf20Sopenharmony_ci } 43398c2ecf20Sopenharmony_ci } 43408c2ecf20Sopenharmony_ci 43418c2ecf20Sopenharmony_ci rcu_read_unlock(); 43428c2ecf20Sopenharmony_ci} 43438c2ecf20Sopenharmony_ci 43448c2ecf20Sopenharmony_cistatic void rtmsg_to_fib6_config(struct net *net, 43458c2ecf20Sopenharmony_ci struct in6_rtmsg *rtmsg, 43468c2ecf20Sopenharmony_ci struct fib6_config *cfg) 43478c2ecf20Sopenharmony_ci{ 43488c2ecf20Sopenharmony_ci *cfg = (struct fib6_config){ 43498c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 43508c2ecf20Sopenharmony_ci : RT6_TABLE_MAIN, 43518c2ecf20Sopenharmony_ci .fc_ifindex = rtmsg->rtmsg_ifindex, 43528c2ecf20Sopenharmony_ci .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER, 43538c2ecf20Sopenharmony_ci .fc_expires = rtmsg->rtmsg_info, 43548c2ecf20Sopenharmony_ci .fc_dst_len = rtmsg->rtmsg_dst_len, 43558c2ecf20Sopenharmony_ci .fc_src_len = rtmsg->rtmsg_src_len, 43568c2ecf20Sopenharmony_ci .fc_flags = rtmsg->rtmsg_flags, 43578c2ecf20Sopenharmony_ci .fc_type = rtmsg->rtmsg_type, 43588c2ecf20Sopenharmony_ci 43598c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = net, 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_ci .fc_dst = rtmsg->rtmsg_dst, 43628c2ecf20Sopenharmony_ci .fc_src = rtmsg->rtmsg_src, 43638c2ecf20Sopenharmony_ci .fc_gateway = rtmsg->rtmsg_gateway, 43648c2ecf20Sopenharmony_ci }; 43658c2ecf20Sopenharmony_ci} 43668c2ecf20Sopenharmony_ci 43678c2ecf20Sopenharmony_ciint ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg) 43688c2ecf20Sopenharmony_ci{ 43698c2ecf20Sopenharmony_ci struct fib6_config cfg; 43708c2ecf20Sopenharmony_ci int err; 43718c2ecf20Sopenharmony_ci 43728c2ecf20Sopenharmony_ci if (cmd != SIOCADDRT && cmd != SIOCDELRT) 43738c2ecf20Sopenharmony_ci return -EINVAL; 43748c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 43758c2ecf20Sopenharmony_ci return -EPERM; 43768c2ecf20Sopenharmony_ci 43778c2ecf20Sopenharmony_ci rtmsg_to_fib6_config(net, rtmsg, &cfg); 43788c2ecf20Sopenharmony_ci 43798c2ecf20Sopenharmony_ci rtnl_lock(); 43808c2ecf20Sopenharmony_ci switch (cmd) { 43818c2ecf20Sopenharmony_ci case SIOCADDRT: 43828c2ecf20Sopenharmony_ci err = ip6_route_add(&cfg, GFP_KERNEL, NULL); 43838c2ecf20Sopenharmony_ci break; 43848c2ecf20Sopenharmony_ci case SIOCDELRT: 43858c2ecf20Sopenharmony_ci err = ip6_route_del(&cfg, NULL); 43868c2ecf20Sopenharmony_ci break; 43878c2ecf20Sopenharmony_ci } 43888c2ecf20Sopenharmony_ci rtnl_unlock(); 43898c2ecf20Sopenharmony_ci return err; 43908c2ecf20Sopenharmony_ci} 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci/* 43938c2ecf20Sopenharmony_ci * Drop the packet on the floor 43948c2ecf20Sopenharmony_ci */ 43958c2ecf20Sopenharmony_ci 43968c2ecf20Sopenharmony_cistatic int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 43978c2ecf20Sopenharmony_ci{ 43988c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 43998c2ecf20Sopenharmony_ci struct net *net = dev_net(dst->dev); 44008c2ecf20Sopenharmony_ci struct inet6_dev *idev; 44018c2ecf20Sopenharmony_ci int type; 44028c2ecf20Sopenharmony_ci 44038c2ecf20Sopenharmony_ci if (netif_is_l3_master(skb->dev) || 44048c2ecf20Sopenharmony_ci dst->dev == net->loopback_dev) 44058c2ecf20Sopenharmony_ci idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); 44068c2ecf20Sopenharmony_ci else 44078c2ecf20Sopenharmony_ci idev = ip6_dst_idev(dst); 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci switch (ipstats_mib_noroutes) { 44108c2ecf20Sopenharmony_ci case IPSTATS_MIB_INNOROUTES: 44118c2ecf20Sopenharmony_ci type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 44128c2ecf20Sopenharmony_ci if (type == IPV6_ADDR_ANY) { 44138c2ecf20Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 44148c2ecf20Sopenharmony_ci break; 44158c2ecf20Sopenharmony_ci } 44168c2ecf20Sopenharmony_ci fallthrough; 44178c2ecf20Sopenharmony_ci case IPSTATS_MIB_OUTNOROUTES: 44188c2ecf20Sopenharmony_ci IP6_INC_STATS(net, idev, ipstats_mib_noroutes); 44198c2ecf20Sopenharmony_ci break; 44208c2ecf20Sopenharmony_ci } 44218c2ecf20Sopenharmony_ci 44228c2ecf20Sopenharmony_ci /* Start over by dropping the dst for l3mdev case */ 44238c2ecf20Sopenharmony_ci if (netif_is_l3_master(skb->dev)) 44248c2ecf20Sopenharmony_ci skb_dst_drop(skb); 44258c2ecf20Sopenharmony_ci 44268c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 44278c2ecf20Sopenharmony_ci kfree_skb(skb); 44288c2ecf20Sopenharmony_ci return 0; 44298c2ecf20Sopenharmony_ci} 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb) 44328c2ecf20Sopenharmony_ci{ 44338c2ecf20Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 44348c2ecf20Sopenharmony_ci} 44358c2ecf20Sopenharmony_ci 44368c2ecf20Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 44378c2ecf20Sopenharmony_ci{ 44388c2ecf20Sopenharmony_ci skb->dev = skb_dst(skb)->dev; 44398c2ecf20Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 44408c2ecf20Sopenharmony_ci} 44418c2ecf20Sopenharmony_ci 44428c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb) 44438c2ecf20Sopenharmony_ci{ 44448c2ecf20Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 44458c2ecf20Sopenharmony_ci} 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 44488c2ecf20Sopenharmony_ci{ 44498c2ecf20Sopenharmony_ci skb->dev = skb_dst(skb)->dev; 44508c2ecf20Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 44518c2ecf20Sopenharmony_ci} 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_ci/* 44548c2ecf20Sopenharmony_ci * Allocate a dst for local (unicast / anycast) address. 44558c2ecf20Sopenharmony_ci */ 44568c2ecf20Sopenharmony_ci 44578c2ecf20Sopenharmony_cistruct fib6_info *addrconf_f6i_alloc(struct net *net, 44588c2ecf20Sopenharmony_ci struct inet6_dev *idev, 44598c2ecf20Sopenharmony_ci const struct in6_addr *addr, 44608c2ecf20Sopenharmony_ci bool anycast, gfp_t gfp_flags) 44618c2ecf20Sopenharmony_ci{ 44628c2ecf20Sopenharmony_ci struct fib6_config cfg = { 44638c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL, 44648c2ecf20Sopenharmony_ci .fc_ifindex = idev->dev->ifindex, 44658c2ecf20Sopenharmony_ci .fc_flags = RTF_UP | RTF_NONEXTHOP, 44668c2ecf20Sopenharmony_ci .fc_dst = *addr, 44678c2ecf20Sopenharmony_ci .fc_dst_len = 128, 44688c2ecf20Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 44698c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = net, 44708c2ecf20Sopenharmony_ci .fc_ignore_dev_down = true, 44718c2ecf20Sopenharmony_ci }; 44728c2ecf20Sopenharmony_ci struct fib6_info *f6i; 44738c2ecf20Sopenharmony_ci 44748c2ecf20Sopenharmony_ci if (anycast) { 44758c2ecf20Sopenharmony_ci cfg.fc_type = RTN_ANYCAST; 44768c2ecf20Sopenharmony_ci cfg.fc_flags |= RTF_ANYCAST; 44778c2ecf20Sopenharmony_ci } else { 44788c2ecf20Sopenharmony_ci cfg.fc_type = RTN_LOCAL; 44798c2ecf20Sopenharmony_ci cfg.fc_flags |= RTF_LOCAL; 44808c2ecf20Sopenharmony_ci } 44818c2ecf20Sopenharmony_ci 44828c2ecf20Sopenharmony_ci f6i = ip6_route_info_create(&cfg, gfp_flags, NULL); 44838c2ecf20Sopenharmony_ci if (!IS_ERR(f6i)) { 44848c2ecf20Sopenharmony_ci f6i->dst_nocount = true; 44858c2ecf20Sopenharmony_ci 44868c2ecf20Sopenharmony_ci if (!anycast && 44878c2ecf20Sopenharmony_ci (net->ipv6.devconf_all->disable_policy || 44888c2ecf20Sopenharmony_ci idev->cnf.disable_policy)) 44898c2ecf20Sopenharmony_ci f6i->dst_nopolicy = true; 44908c2ecf20Sopenharmony_ci } 44918c2ecf20Sopenharmony_ci 44928c2ecf20Sopenharmony_ci return f6i; 44938c2ecf20Sopenharmony_ci} 44948c2ecf20Sopenharmony_ci 44958c2ecf20Sopenharmony_ci/* remove deleted ip from prefsrc entries */ 44968c2ecf20Sopenharmony_cistruct arg_dev_net_ip { 44978c2ecf20Sopenharmony_ci struct net_device *dev; 44988c2ecf20Sopenharmony_ci struct net *net; 44998c2ecf20Sopenharmony_ci struct in6_addr *addr; 45008c2ecf20Sopenharmony_ci}; 45018c2ecf20Sopenharmony_ci 45028c2ecf20Sopenharmony_cistatic int fib6_remove_prefsrc(struct fib6_info *rt, void *arg) 45038c2ecf20Sopenharmony_ci{ 45048c2ecf20Sopenharmony_ci struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 45058c2ecf20Sopenharmony_ci struct net *net = ((struct arg_dev_net_ip *)arg)->net; 45068c2ecf20Sopenharmony_ci struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 45078c2ecf20Sopenharmony_ci 45088c2ecf20Sopenharmony_ci if (!rt->nh && 45098c2ecf20Sopenharmony_ci ((void *)rt->fib6_nh->fib_nh_dev == dev || !dev) && 45108c2ecf20Sopenharmony_ci rt != net->ipv6.fib6_null_entry && 45118c2ecf20Sopenharmony_ci ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) { 45128c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 45138c2ecf20Sopenharmony_ci /* remove prefsrc entry */ 45148c2ecf20Sopenharmony_ci rt->fib6_prefsrc.plen = 0; 45158c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 45168c2ecf20Sopenharmony_ci } 45178c2ecf20Sopenharmony_ci return 0; 45188c2ecf20Sopenharmony_ci} 45198c2ecf20Sopenharmony_ci 45208c2ecf20Sopenharmony_civoid rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 45218c2ecf20Sopenharmony_ci{ 45228c2ecf20Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 45238c2ecf20Sopenharmony_ci struct arg_dev_net_ip adni = { 45248c2ecf20Sopenharmony_ci .dev = ifp->idev->dev, 45258c2ecf20Sopenharmony_ci .net = net, 45268c2ecf20Sopenharmony_ci .addr = &ifp->addr, 45278c2ecf20Sopenharmony_ci }; 45288c2ecf20Sopenharmony_ci fib6_clean_all(net, fib6_remove_prefsrc, &adni); 45298c2ecf20Sopenharmony_ci} 45308c2ecf20Sopenharmony_ci 45318c2ecf20Sopenharmony_ci#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT) 45328c2ecf20Sopenharmony_ci 45338c2ecf20Sopenharmony_ci/* Remove routers and update dst entries when gateway turn into host. */ 45348c2ecf20Sopenharmony_cistatic int fib6_clean_tohost(struct fib6_info *rt, void *arg) 45358c2ecf20Sopenharmony_ci{ 45368c2ecf20Sopenharmony_ci struct in6_addr *gateway = (struct in6_addr *)arg; 45378c2ecf20Sopenharmony_ci struct fib6_nh *nh; 45388c2ecf20Sopenharmony_ci 45398c2ecf20Sopenharmony_ci /* RA routes do not use nexthops */ 45408c2ecf20Sopenharmony_ci if (rt->nh) 45418c2ecf20Sopenharmony_ci return 0; 45428c2ecf20Sopenharmony_ci 45438c2ecf20Sopenharmony_ci nh = rt->fib6_nh; 45448c2ecf20Sopenharmony_ci if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) && 45458c2ecf20Sopenharmony_ci nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6)) 45468c2ecf20Sopenharmony_ci return -1; 45478c2ecf20Sopenharmony_ci 45488c2ecf20Sopenharmony_ci /* Further clean up cached routes in exception table. 45498c2ecf20Sopenharmony_ci * This is needed because cached route may have a different 45508c2ecf20Sopenharmony_ci * gateway than its 'parent' in the case of an ip redirect. 45518c2ecf20Sopenharmony_ci */ 45528c2ecf20Sopenharmony_ci fib6_nh_exceptions_clean_tohost(nh, gateway); 45538c2ecf20Sopenharmony_ci 45548c2ecf20Sopenharmony_ci return 0; 45558c2ecf20Sopenharmony_ci} 45568c2ecf20Sopenharmony_ci 45578c2ecf20Sopenharmony_civoid rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 45588c2ecf20Sopenharmony_ci{ 45598c2ecf20Sopenharmony_ci fib6_clean_all(net, fib6_clean_tohost, gateway); 45608c2ecf20Sopenharmony_ci} 45618c2ecf20Sopenharmony_ci 45628c2ecf20Sopenharmony_cistruct arg_netdev_event { 45638c2ecf20Sopenharmony_ci const struct net_device *dev; 45648c2ecf20Sopenharmony_ci union { 45658c2ecf20Sopenharmony_ci unsigned char nh_flags; 45668c2ecf20Sopenharmony_ci unsigned long event; 45678c2ecf20Sopenharmony_ci }; 45688c2ecf20Sopenharmony_ci}; 45698c2ecf20Sopenharmony_ci 45708c2ecf20Sopenharmony_cistatic struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt) 45718c2ecf20Sopenharmony_ci{ 45728c2ecf20Sopenharmony_ci struct fib6_info *iter; 45738c2ecf20Sopenharmony_ci struct fib6_node *fn; 45748c2ecf20Sopenharmony_ci 45758c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(rt->fib6_node, 45768c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 45778c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(fn->leaf, 45788c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 45798c2ecf20Sopenharmony_ci while (iter) { 45808c2ecf20Sopenharmony_ci if (iter->fib6_metric == rt->fib6_metric && 45818c2ecf20Sopenharmony_ci rt6_qualify_for_ecmp(iter)) 45828c2ecf20Sopenharmony_ci return iter; 45838c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(iter->fib6_next, 45848c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 45858c2ecf20Sopenharmony_ci } 45868c2ecf20Sopenharmony_ci 45878c2ecf20Sopenharmony_ci return NULL; 45888c2ecf20Sopenharmony_ci} 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci/* only called for fib entries with builtin fib6_nh */ 45918c2ecf20Sopenharmony_cistatic bool rt6_is_dead(const struct fib6_info *rt) 45928c2ecf20Sopenharmony_ci{ 45938c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD || 45948c2ecf20Sopenharmony_ci (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN && 45958c2ecf20Sopenharmony_ci ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev))) 45968c2ecf20Sopenharmony_ci return true; 45978c2ecf20Sopenharmony_ci 45988c2ecf20Sopenharmony_ci return false; 45998c2ecf20Sopenharmony_ci} 46008c2ecf20Sopenharmony_ci 46018c2ecf20Sopenharmony_cistatic int rt6_multipath_total_weight(const struct fib6_info *rt) 46028c2ecf20Sopenharmony_ci{ 46038c2ecf20Sopenharmony_ci struct fib6_info *iter; 46048c2ecf20Sopenharmony_ci int total = 0; 46058c2ecf20Sopenharmony_ci 46068c2ecf20Sopenharmony_ci if (!rt6_is_dead(rt)) 46078c2ecf20Sopenharmony_ci total += rt->fib6_nh->fib_nh_weight; 46088c2ecf20Sopenharmony_ci 46098c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { 46108c2ecf20Sopenharmony_ci if (!rt6_is_dead(iter)) 46118c2ecf20Sopenharmony_ci total += iter->fib6_nh->fib_nh_weight; 46128c2ecf20Sopenharmony_ci } 46138c2ecf20Sopenharmony_ci 46148c2ecf20Sopenharmony_ci return total; 46158c2ecf20Sopenharmony_ci} 46168c2ecf20Sopenharmony_ci 46178c2ecf20Sopenharmony_cistatic void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total) 46188c2ecf20Sopenharmony_ci{ 46198c2ecf20Sopenharmony_ci int upper_bound = -1; 46208c2ecf20Sopenharmony_ci 46218c2ecf20Sopenharmony_ci if (!rt6_is_dead(rt)) { 46228c2ecf20Sopenharmony_ci *weight += rt->fib6_nh->fib_nh_weight; 46238c2ecf20Sopenharmony_ci upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31, 46248c2ecf20Sopenharmony_ci total) - 1; 46258c2ecf20Sopenharmony_ci } 46268c2ecf20Sopenharmony_ci atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound); 46278c2ecf20Sopenharmony_ci} 46288c2ecf20Sopenharmony_ci 46298c2ecf20Sopenharmony_cistatic void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total) 46308c2ecf20Sopenharmony_ci{ 46318c2ecf20Sopenharmony_ci struct fib6_info *iter; 46328c2ecf20Sopenharmony_ci int weight = 0; 46338c2ecf20Sopenharmony_ci 46348c2ecf20Sopenharmony_ci rt6_upper_bound_set(rt, &weight, total); 46358c2ecf20Sopenharmony_ci 46368c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 46378c2ecf20Sopenharmony_ci rt6_upper_bound_set(iter, &weight, total); 46388c2ecf20Sopenharmony_ci} 46398c2ecf20Sopenharmony_ci 46408c2ecf20Sopenharmony_civoid rt6_multipath_rebalance(struct fib6_info *rt) 46418c2ecf20Sopenharmony_ci{ 46428c2ecf20Sopenharmony_ci struct fib6_info *first; 46438c2ecf20Sopenharmony_ci int total; 46448c2ecf20Sopenharmony_ci 46458c2ecf20Sopenharmony_ci /* In case the entire multipath route was marked for flushing, 46468c2ecf20Sopenharmony_ci * then there is no need to rebalance upon the removal of every 46478c2ecf20Sopenharmony_ci * sibling route. 46488c2ecf20Sopenharmony_ci */ 46498c2ecf20Sopenharmony_ci if (!rt->fib6_nsiblings || rt->should_flush) 46508c2ecf20Sopenharmony_ci return; 46518c2ecf20Sopenharmony_ci 46528c2ecf20Sopenharmony_ci /* During lookup routes are evaluated in order, so we need to 46538c2ecf20Sopenharmony_ci * make sure upper bounds are assigned from the first sibling 46548c2ecf20Sopenharmony_ci * onwards. 46558c2ecf20Sopenharmony_ci */ 46568c2ecf20Sopenharmony_ci first = rt6_multipath_first_sibling(rt); 46578c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!first)) 46588c2ecf20Sopenharmony_ci return; 46598c2ecf20Sopenharmony_ci 46608c2ecf20Sopenharmony_ci total = rt6_multipath_total_weight(first); 46618c2ecf20Sopenharmony_ci rt6_multipath_upper_bound_set(first, total); 46628c2ecf20Sopenharmony_ci} 46638c2ecf20Sopenharmony_ci 46648c2ecf20Sopenharmony_cistatic int fib6_ifup(struct fib6_info *rt, void *p_arg) 46658c2ecf20Sopenharmony_ci{ 46668c2ecf20Sopenharmony_ci const struct arg_netdev_event *arg = p_arg; 46678c2ecf20Sopenharmony_ci struct net *net = dev_net(arg->dev); 46688c2ecf20Sopenharmony_ci 46698c2ecf20Sopenharmony_ci if (rt != net->ipv6.fib6_null_entry && !rt->nh && 46708c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_dev == arg->dev) { 46718c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags; 46728c2ecf20Sopenharmony_ci fib6_update_sernum_upto_root(net, rt); 46738c2ecf20Sopenharmony_ci rt6_multipath_rebalance(rt); 46748c2ecf20Sopenharmony_ci } 46758c2ecf20Sopenharmony_ci 46768c2ecf20Sopenharmony_ci return 0; 46778c2ecf20Sopenharmony_ci} 46788c2ecf20Sopenharmony_ci 46798c2ecf20Sopenharmony_civoid rt6_sync_up(struct net_device *dev, unsigned char nh_flags) 46808c2ecf20Sopenharmony_ci{ 46818c2ecf20Sopenharmony_ci struct arg_netdev_event arg = { 46828c2ecf20Sopenharmony_ci .dev = dev, 46838c2ecf20Sopenharmony_ci { 46848c2ecf20Sopenharmony_ci .nh_flags = nh_flags, 46858c2ecf20Sopenharmony_ci }, 46868c2ecf20Sopenharmony_ci }; 46878c2ecf20Sopenharmony_ci 46888c2ecf20Sopenharmony_ci if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev)) 46898c2ecf20Sopenharmony_ci arg.nh_flags |= RTNH_F_LINKDOWN; 46908c2ecf20Sopenharmony_ci 46918c2ecf20Sopenharmony_ci fib6_clean_all(dev_net(dev), fib6_ifup, &arg); 46928c2ecf20Sopenharmony_ci} 46938c2ecf20Sopenharmony_ci 46948c2ecf20Sopenharmony_ci/* only called for fib entries with inline fib6_nh */ 46958c2ecf20Sopenharmony_cistatic bool rt6_multipath_uses_dev(const struct fib6_info *rt, 46968c2ecf20Sopenharmony_ci const struct net_device *dev) 46978c2ecf20Sopenharmony_ci{ 46988c2ecf20Sopenharmony_ci struct fib6_info *iter; 46998c2ecf20Sopenharmony_ci 47008c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == dev) 47018c2ecf20Sopenharmony_ci return true; 47028c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 47038c2ecf20Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == dev) 47048c2ecf20Sopenharmony_ci return true; 47058c2ecf20Sopenharmony_ci 47068c2ecf20Sopenharmony_ci return false; 47078c2ecf20Sopenharmony_ci} 47088c2ecf20Sopenharmony_ci 47098c2ecf20Sopenharmony_cistatic void rt6_multipath_flush(struct fib6_info *rt) 47108c2ecf20Sopenharmony_ci{ 47118c2ecf20Sopenharmony_ci struct fib6_info *iter; 47128c2ecf20Sopenharmony_ci 47138c2ecf20Sopenharmony_ci rt->should_flush = 1; 47148c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 47158c2ecf20Sopenharmony_ci iter->should_flush = 1; 47168c2ecf20Sopenharmony_ci} 47178c2ecf20Sopenharmony_ci 47188c2ecf20Sopenharmony_cistatic unsigned int rt6_multipath_dead_count(const struct fib6_info *rt, 47198c2ecf20Sopenharmony_ci const struct net_device *down_dev) 47208c2ecf20Sopenharmony_ci{ 47218c2ecf20Sopenharmony_ci struct fib6_info *iter; 47228c2ecf20Sopenharmony_ci unsigned int dead = 0; 47238c2ecf20Sopenharmony_ci 47248c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == down_dev || 47258c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD) 47268c2ecf20Sopenharmony_ci dead++; 47278c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 47288c2ecf20Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == down_dev || 47298c2ecf20Sopenharmony_ci iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD) 47308c2ecf20Sopenharmony_ci dead++; 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_ci return dead; 47338c2ecf20Sopenharmony_ci} 47348c2ecf20Sopenharmony_ci 47358c2ecf20Sopenharmony_cistatic void rt6_multipath_nh_flags_set(struct fib6_info *rt, 47368c2ecf20Sopenharmony_ci const struct net_device *dev, 47378c2ecf20Sopenharmony_ci unsigned char nh_flags) 47388c2ecf20Sopenharmony_ci{ 47398c2ecf20Sopenharmony_ci struct fib6_info *iter; 47408c2ecf20Sopenharmony_ci 47418c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == dev) 47428c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_flags |= nh_flags; 47438c2ecf20Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 47448c2ecf20Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == dev) 47458c2ecf20Sopenharmony_ci iter->fib6_nh->fib_nh_flags |= nh_flags; 47468c2ecf20Sopenharmony_ci} 47478c2ecf20Sopenharmony_ci 47488c2ecf20Sopenharmony_ci/* called with write lock held for table with rt */ 47498c2ecf20Sopenharmony_cistatic int fib6_ifdown(struct fib6_info *rt, void *p_arg) 47508c2ecf20Sopenharmony_ci{ 47518c2ecf20Sopenharmony_ci const struct arg_netdev_event *arg = p_arg; 47528c2ecf20Sopenharmony_ci const struct net_device *dev = arg->dev; 47538c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 47548c2ecf20Sopenharmony_ci 47558c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry || rt->nh) 47568c2ecf20Sopenharmony_ci return 0; 47578c2ecf20Sopenharmony_ci 47588c2ecf20Sopenharmony_ci switch (arg->event) { 47598c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 47608c2ecf20Sopenharmony_ci return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0; 47618c2ecf20Sopenharmony_ci case NETDEV_DOWN: 47628c2ecf20Sopenharmony_ci if (rt->should_flush) 47638c2ecf20Sopenharmony_ci return -1; 47648c2ecf20Sopenharmony_ci if (!rt->fib6_nsiblings) 47658c2ecf20Sopenharmony_ci return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0; 47668c2ecf20Sopenharmony_ci if (rt6_multipath_uses_dev(rt, dev)) { 47678c2ecf20Sopenharmony_ci unsigned int count; 47688c2ecf20Sopenharmony_ci 47698c2ecf20Sopenharmony_ci count = rt6_multipath_dead_count(rt, dev); 47708c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings + 1 == count) { 47718c2ecf20Sopenharmony_ci rt6_multipath_flush(rt); 47728c2ecf20Sopenharmony_ci return -1; 47738c2ecf20Sopenharmony_ci } 47748c2ecf20Sopenharmony_ci rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD | 47758c2ecf20Sopenharmony_ci RTNH_F_LINKDOWN); 47768c2ecf20Sopenharmony_ci fib6_update_sernum(net, rt); 47778c2ecf20Sopenharmony_ci rt6_multipath_rebalance(rt); 47788c2ecf20Sopenharmony_ci } 47798c2ecf20Sopenharmony_ci return -2; 47808c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 47818c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev != dev || 47828c2ecf20Sopenharmony_ci rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) 47838c2ecf20Sopenharmony_ci break; 47848c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 47858c2ecf20Sopenharmony_ci rt6_multipath_rebalance(rt); 47868c2ecf20Sopenharmony_ci break; 47878c2ecf20Sopenharmony_ci } 47888c2ecf20Sopenharmony_ci 47898c2ecf20Sopenharmony_ci return 0; 47908c2ecf20Sopenharmony_ci} 47918c2ecf20Sopenharmony_ci 47928c2ecf20Sopenharmony_civoid rt6_sync_down_dev(struct net_device *dev, unsigned long event) 47938c2ecf20Sopenharmony_ci{ 47948c2ecf20Sopenharmony_ci struct arg_netdev_event arg = { 47958c2ecf20Sopenharmony_ci .dev = dev, 47968c2ecf20Sopenharmony_ci { 47978c2ecf20Sopenharmony_ci .event = event, 47988c2ecf20Sopenharmony_ci }, 47998c2ecf20Sopenharmony_ci }; 48008c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 48018c2ecf20Sopenharmony_ci 48028c2ecf20Sopenharmony_ci if (net->ipv6.sysctl.skip_notify_on_dev_down) 48038c2ecf20Sopenharmony_ci fib6_clean_all_skip_notify(net, fib6_ifdown, &arg); 48048c2ecf20Sopenharmony_ci else 48058c2ecf20Sopenharmony_ci fib6_clean_all(net, fib6_ifdown, &arg); 48068c2ecf20Sopenharmony_ci} 48078c2ecf20Sopenharmony_ci 48088c2ecf20Sopenharmony_civoid rt6_disable_ip(struct net_device *dev, unsigned long event) 48098c2ecf20Sopenharmony_ci{ 48108c2ecf20Sopenharmony_ci rt6_sync_down_dev(dev, event); 48118c2ecf20Sopenharmony_ci rt6_uncached_list_flush_dev(dev_net(dev), dev); 48128c2ecf20Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 48138c2ecf20Sopenharmony_ci} 48148c2ecf20Sopenharmony_ci 48158c2ecf20Sopenharmony_cistruct rt6_mtu_change_arg { 48168c2ecf20Sopenharmony_ci struct net_device *dev; 48178c2ecf20Sopenharmony_ci unsigned int mtu; 48188c2ecf20Sopenharmony_ci struct fib6_info *f6i; 48198c2ecf20Sopenharmony_ci}; 48208c2ecf20Sopenharmony_ci 48218c2ecf20Sopenharmony_cistatic int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg) 48228c2ecf20Sopenharmony_ci{ 48238c2ecf20Sopenharmony_ci struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg; 48248c2ecf20Sopenharmony_ci struct fib6_info *f6i = arg->f6i; 48258c2ecf20Sopenharmony_ci 48268c2ecf20Sopenharmony_ci /* For administrative MTU increase, there is no way to discover 48278c2ecf20Sopenharmony_ci * IPv6 PMTU increase, so PMTU increase should be updated here. 48288c2ecf20Sopenharmony_ci * Since RFC 1981 doesn't include administrative MTU increase 48298c2ecf20Sopenharmony_ci * update PMTU increase is a MUST. (i.e. jumbo frame) 48308c2ecf20Sopenharmony_ci */ 48318c2ecf20Sopenharmony_ci if (nh->fib_nh_dev == arg->dev) { 48328c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(arg->dev); 48338c2ecf20Sopenharmony_ci u32 mtu = f6i->fib6_pmtu; 48348c2ecf20Sopenharmony_ci 48358c2ecf20Sopenharmony_ci if (mtu >= arg->mtu || 48368c2ecf20Sopenharmony_ci (mtu < arg->mtu && mtu == idev->cnf.mtu6)) 48378c2ecf20Sopenharmony_ci fib6_metric_set(f6i, RTAX_MTU, arg->mtu); 48388c2ecf20Sopenharmony_ci 48398c2ecf20Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 48408c2ecf20Sopenharmony_ci rt6_exceptions_update_pmtu(idev, nh, arg->mtu); 48418c2ecf20Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 48428c2ecf20Sopenharmony_ci } 48438c2ecf20Sopenharmony_ci 48448c2ecf20Sopenharmony_ci return 0; 48458c2ecf20Sopenharmony_ci} 48468c2ecf20Sopenharmony_ci 48478c2ecf20Sopenharmony_cistatic int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg) 48488c2ecf20Sopenharmony_ci{ 48498c2ecf20Sopenharmony_ci struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 48508c2ecf20Sopenharmony_ci struct inet6_dev *idev; 48518c2ecf20Sopenharmony_ci 48528c2ecf20Sopenharmony_ci /* In IPv6 pmtu discovery is not optional, 48538c2ecf20Sopenharmony_ci so that RTAX_MTU lock cannot disable it. 48548c2ecf20Sopenharmony_ci We still use this lock to block changes 48558c2ecf20Sopenharmony_ci caused by addrconf/ndisc. 48568c2ecf20Sopenharmony_ci */ 48578c2ecf20Sopenharmony_ci 48588c2ecf20Sopenharmony_ci idev = __in6_dev_get(arg->dev); 48598c2ecf20Sopenharmony_ci if (!idev) 48608c2ecf20Sopenharmony_ci return 0; 48618c2ecf20Sopenharmony_ci 48628c2ecf20Sopenharmony_ci if (fib6_metric_locked(f6i, RTAX_MTU)) 48638c2ecf20Sopenharmony_ci return 0; 48648c2ecf20Sopenharmony_ci 48658c2ecf20Sopenharmony_ci arg->f6i = f6i; 48668c2ecf20Sopenharmony_ci if (f6i->nh) { 48678c2ecf20Sopenharmony_ci /* fib6_nh_mtu_change only returns 0, so this is safe */ 48688c2ecf20Sopenharmony_ci return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change, 48698c2ecf20Sopenharmony_ci arg); 48708c2ecf20Sopenharmony_ci } 48718c2ecf20Sopenharmony_ci 48728c2ecf20Sopenharmony_ci return fib6_nh_mtu_change(f6i->fib6_nh, arg); 48738c2ecf20Sopenharmony_ci} 48748c2ecf20Sopenharmony_ci 48758c2ecf20Sopenharmony_civoid rt6_mtu_change(struct net_device *dev, unsigned int mtu) 48768c2ecf20Sopenharmony_ci{ 48778c2ecf20Sopenharmony_ci struct rt6_mtu_change_arg arg = { 48788c2ecf20Sopenharmony_ci .dev = dev, 48798c2ecf20Sopenharmony_ci .mtu = mtu, 48808c2ecf20Sopenharmony_ci }; 48818c2ecf20Sopenharmony_ci 48828c2ecf20Sopenharmony_ci fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 48838c2ecf20Sopenharmony_ci} 48848c2ecf20Sopenharmony_ci 48858c2ecf20Sopenharmony_cistatic const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 48868c2ecf20Sopenharmony_ci [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 }, 48878c2ecf20Sopenharmony_ci [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 48888c2ecf20Sopenharmony_ci [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) }, 48898c2ecf20Sopenharmony_ci [RTA_OIF] = { .type = NLA_U32 }, 48908c2ecf20Sopenharmony_ci [RTA_IIF] = { .type = NLA_U32 }, 48918c2ecf20Sopenharmony_ci [RTA_PRIORITY] = { .type = NLA_U32 }, 48928c2ecf20Sopenharmony_ci [RTA_METRICS] = { .type = NLA_NESTED }, 48938c2ecf20Sopenharmony_ci [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 48948c2ecf20Sopenharmony_ci [RTA_PREF] = { .type = NLA_U8 }, 48958c2ecf20Sopenharmony_ci [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 48968c2ecf20Sopenharmony_ci [RTA_ENCAP] = { .type = NLA_NESTED }, 48978c2ecf20Sopenharmony_ci [RTA_EXPIRES] = { .type = NLA_U32 }, 48988c2ecf20Sopenharmony_ci [RTA_UID] = { .type = NLA_U32 }, 48998c2ecf20Sopenharmony_ci [RTA_MARK] = { .type = NLA_U32 }, 49008c2ecf20Sopenharmony_ci [RTA_TABLE] = { .type = NLA_U32 }, 49018c2ecf20Sopenharmony_ci [RTA_IP_PROTO] = { .type = NLA_U8 }, 49028c2ecf20Sopenharmony_ci [RTA_SPORT] = { .type = NLA_U16 }, 49038c2ecf20Sopenharmony_ci [RTA_DPORT] = { .type = NLA_U16 }, 49048c2ecf20Sopenharmony_ci [RTA_NH_ID] = { .type = NLA_U32 }, 49058c2ecf20Sopenharmony_ci}; 49068c2ecf20Sopenharmony_ci 49078c2ecf20Sopenharmony_cistatic int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 49088c2ecf20Sopenharmony_ci struct fib6_config *cfg, 49098c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 49108c2ecf20Sopenharmony_ci{ 49118c2ecf20Sopenharmony_ci struct rtmsg *rtm; 49128c2ecf20Sopenharmony_ci struct nlattr *tb[RTA_MAX+1]; 49138c2ecf20Sopenharmony_ci unsigned int pref; 49148c2ecf20Sopenharmony_ci int err; 49158c2ecf20Sopenharmony_ci 49168c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 49178c2ecf20Sopenharmony_ci rtm_ipv6_policy, extack); 49188c2ecf20Sopenharmony_ci if (err < 0) 49198c2ecf20Sopenharmony_ci goto errout; 49208c2ecf20Sopenharmony_ci 49218c2ecf20Sopenharmony_ci err = -EINVAL; 49228c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ci *cfg = (struct fib6_config){ 49258c2ecf20Sopenharmony_ci .fc_table = rtm->rtm_table, 49268c2ecf20Sopenharmony_ci .fc_dst_len = rtm->rtm_dst_len, 49278c2ecf20Sopenharmony_ci .fc_src_len = rtm->rtm_src_len, 49288c2ecf20Sopenharmony_ci .fc_flags = RTF_UP, 49298c2ecf20Sopenharmony_ci .fc_protocol = rtm->rtm_protocol, 49308c2ecf20Sopenharmony_ci .fc_type = rtm->rtm_type, 49318c2ecf20Sopenharmony_ci 49328c2ecf20Sopenharmony_ci .fc_nlinfo.portid = NETLINK_CB(skb).portid, 49338c2ecf20Sopenharmony_ci .fc_nlinfo.nlh = nlh, 49348c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = sock_net(skb->sk), 49358c2ecf20Sopenharmony_ci }; 49368c2ecf20Sopenharmony_ci 49378c2ecf20Sopenharmony_ci if (rtm->rtm_type == RTN_UNREACHABLE || 49388c2ecf20Sopenharmony_ci rtm->rtm_type == RTN_BLACKHOLE || 49398c2ecf20Sopenharmony_ci rtm->rtm_type == RTN_PROHIBIT || 49408c2ecf20Sopenharmony_ci rtm->rtm_type == RTN_THROW) 49418c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_REJECT; 49428c2ecf20Sopenharmony_ci 49438c2ecf20Sopenharmony_ci if (rtm->rtm_type == RTN_LOCAL) 49448c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_LOCAL; 49458c2ecf20Sopenharmony_ci 49468c2ecf20Sopenharmony_ci if (rtm->rtm_flags & RTM_F_CLONED) 49478c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_CACHE; 49488c2ecf20Sopenharmony_ci 49498c2ecf20Sopenharmony_ci cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); 49508c2ecf20Sopenharmony_ci 49518c2ecf20Sopenharmony_ci if (tb[RTA_NH_ID]) { 49528c2ecf20Sopenharmony_ci if (tb[RTA_GATEWAY] || tb[RTA_OIF] || 49538c2ecf20Sopenharmony_ci tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) { 49548c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 49558c2ecf20Sopenharmony_ci "Nexthop specification and nexthop id are mutually exclusive"); 49568c2ecf20Sopenharmony_ci goto errout; 49578c2ecf20Sopenharmony_ci } 49588c2ecf20Sopenharmony_ci cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]); 49598c2ecf20Sopenharmony_ci } 49608c2ecf20Sopenharmony_ci 49618c2ecf20Sopenharmony_ci if (tb[RTA_GATEWAY]) { 49628c2ecf20Sopenharmony_ci cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 49638c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_GATEWAY; 49648c2ecf20Sopenharmony_ci } 49658c2ecf20Sopenharmony_ci if (tb[RTA_VIA]) { 49668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute"); 49678c2ecf20Sopenharmony_ci goto errout; 49688c2ecf20Sopenharmony_ci } 49698c2ecf20Sopenharmony_ci 49708c2ecf20Sopenharmony_ci if (tb[RTA_DST]) { 49718c2ecf20Sopenharmony_ci int plen = (rtm->rtm_dst_len + 7) >> 3; 49728c2ecf20Sopenharmony_ci 49738c2ecf20Sopenharmony_ci if (nla_len(tb[RTA_DST]) < plen) 49748c2ecf20Sopenharmony_ci goto errout; 49758c2ecf20Sopenharmony_ci 49768c2ecf20Sopenharmony_ci nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 49778c2ecf20Sopenharmony_ci } 49788c2ecf20Sopenharmony_ci 49798c2ecf20Sopenharmony_ci if (tb[RTA_SRC]) { 49808c2ecf20Sopenharmony_ci int plen = (rtm->rtm_src_len + 7) >> 3; 49818c2ecf20Sopenharmony_ci 49828c2ecf20Sopenharmony_ci if (nla_len(tb[RTA_SRC]) < plen) 49838c2ecf20Sopenharmony_ci goto errout; 49848c2ecf20Sopenharmony_ci 49858c2ecf20Sopenharmony_ci nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 49868c2ecf20Sopenharmony_ci } 49878c2ecf20Sopenharmony_ci 49888c2ecf20Sopenharmony_ci if (tb[RTA_PREFSRC]) 49898c2ecf20Sopenharmony_ci cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 49908c2ecf20Sopenharmony_ci 49918c2ecf20Sopenharmony_ci if (tb[RTA_OIF]) 49928c2ecf20Sopenharmony_ci cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 49938c2ecf20Sopenharmony_ci 49948c2ecf20Sopenharmony_ci if (tb[RTA_PRIORITY]) 49958c2ecf20Sopenharmony_ci cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 49968c2ecf20Sopenharmony_ci 49978c2ecf20Sopenharmony_ci if (tb[RTA_METRICS]) { 49988c2ecf20Sopenharmony_ci cfg->fc_mx = nla_data(tb[RTA_METRICS]); 49998c2ecf20Sopenharmony_ci cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 50008c2ecf20Sopenharmony_ci } 50018c2ecf20Sopenharmony_ci 50028c2ecf20Sopenharmony_ci if (tb[RTA_TABLE]) 50038c2ecf20Sopenharmony_ci cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 50048c2ecf20Sopenharmony_ci 50058c2ecf20Sopenharmony_ci if (tb[RTA_MULTIPATH]) { 50068c2ecf20Sopenharmony_ci cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 50078c2ecf20Sopenharmony_ci cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 50088c2ecf20Sopenharmony_ci 50098c2ecf20Sopenharmony_ci err = lwtunnel_valid_encap_type_attr(cfg->fc_mp, 50108c2ecf20Sopenharmony_ci cfg->fc_mp_len, extack); 50118c2ecf20Sopenharmony_ci if (err < 0) 50128c2ecf20Sopenharmony_ci goto errout; 50138c2ecf20Sopenharmony_ci } 50148c2ecf20Sopenharmony_ci 50158c2ecf20Sopenharmony_ci if (tb[RTA_PREF]) { 50168c2ecf20Sopenharmony_ci pref = nla_get_u8(tb[RTA_PREF]); 50178c2ecf20Sopenharmony_ci if (pref != ICMPV6_ROUTER_PREF_LOW && 50188c2ecf20Sopenharmony_ci pref != ICMPV6_ROUTER_PREF_HIGH) 50198c2ecf20Sopenharmony_ci pref = ICMPV6_ROUTER_PREF_MEDIUM; 50208c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_PREF(pref); 50218c2ecf20Sopenharmony_ci } 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_ci if (tb[RTA_ENCAP]) 50248c2ecf20Sopenharmony_ci cfg->fc_encap = tb[RTA_ENCAP]; 50258c2ecf20Sopenharmony_ci 50268c2ecf20Sopenharmony_ci if (tb[RTA_ENCAP_TYPE]) { 50278c2ecf20Sopenharmony_ci cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 50288c2ecf20Sopenharmony_ci 50298c2ecf20Sopenharmony_ci err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack); 50308c2ecf20Sopenharmony_ci if (err < 0) 50318c2ecf20Sopenharmony_ci goto errout; 50328c2ecf20Sopenharmony_ci } 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_ci if (tb[RTA_EXPIRES]) { 50358c2ecf20Sopenharmony_ci unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 50368c2ecf20Sopenharmony_ci 50378c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 50388c2ecf20Sopenharmony_ci cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 50398c2ecf20Sopenharmony_ci cfg->fc_flags |= RTF_EXPIRES; 50408c2ecf20Sopenharmony_ci } 50418c2ecf20Sopenharmony_ci } 50428c2ecf20Sopenharmony_ci 50438c2ecf20Sopenharmony_ci err = 0; 50448c2ecf20Sopenharmony_cierrout: 50458c2ecf20Sopenharmony_ci return err; 50468c2ecf20Sopenharmony_ci} 50478c2ecf20Sopenharmony_ci 50488c2ecf20Sopenharmony_cistruct rt6_nh { 50498c2ecf20Sopenharmony_ci struct fib6_info *fib6_info; 50508c2ecf20Sopenharmony_ci struct fib6_config r_cfg; 50518c2ecf20Sopenharmony_ci struct list_head next; 50528c2ecf20Sopenharmony_ci}; 50538c2ecf20Sopenharmony_ci 50548c2ecf20Sopenharmony_cistatic int ip6_route_info_append(struct net *net, 50558c2ecf20Sopenharmony_ci struct list_head *rt6_nh_list, 50568c2ecf20Sopenharmony_ci struct fib6_info *rt, 50578c2ecf20Sopenharmony_ci struct fib6_config *r_cfg) 50588c2ecf20Sopenharmony_ci{ 50598c2ecf20Sopenharmony_ci struct rt6_nh *nh; 50608c2ecf20Sopenharmony_ci int err = -EEXIST; 50618c2ecf20Sopenharmony_ci 50628c2ecf20Sopenharmony_ci list_for_each_entry(nh, rt6_nh_list, next) { 50638c2ecf20Sopenharmony_ci /* check if fib6_info already exists */ 50648c2ecf20Sopenharmony_ci if (rt6_duplicate_nexthop(nh->fib6_info, rt)) 50658c2ecf20Sopenharmony_ci return err; 50668c2ecf20Sopenharmony_ci } 50678c2ecf20Sopenharmony_ci 50688c2ecf20Sopenharmony_ci nh = kzalloc(sizeof(*nh), GFP_KERNEL); 50698c2ecf20Sopenharmony_ci if (!nh) 50708c2ecf20Sopenharmony_ci return -ENOMEM; 50718c2ecf20Sopenharmony_ci nh->fib6_info = rt; 50728c2ecf20Sopenharmony_ci memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 50738c2ecf20Sopenharmony_ci list_add_tail(&nh->next, rt6_nh_list); 50748c2ecf20Sopenharmony_ci 50758c2ecf20Sopenharmony_ci return 0; 50768c2ecf20Sopenharmony_ci} 50778c2ecf20Sopenharmony_ci 50788c2ecf20Sopenharmony_cistatic void ip6_route_mpath_notify(struct fib6_info *rt, 50798c2ecf20Sopenharmony_ci struct fib6_info *rt_last, 50808c2ecf20Sopenharmony_ci struct nl_info *info, 50818c2ecf20Sopenharmony_ci __u16 nlflags) 50828c2ecf20Sopenharmony_ci{ 50838c2ecf20Sopenharmony_ci /* if this is an APPEND route, then rt points to the first route 50848c2ecf20Sopenharmony_ci * inserted and rt_last points to last route inserted. Userspace 50858c2ecf20Sopenharmony_ci * wants a consistent dump of the route which starts at the first 50868c2ecf20Sopenharmony_ci * nexthop. Since sibling routes are always added at the end of 50878c2ecf20Sopenharmony_ci * the list, find the first sibling of the last route appended 50888c2ecf20Sopenharmony_ci */ 50898c2ecf20Sopenharmony_ci if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) { 50908c2ecf20Sopenharmony_ci rt = list_first_entry(&rt_last->fib6_siblings, 50918c2ecf20Sopenharmony_ci struct fib6_info, 50928c2ecf20Sopenharmony_ci fib6_siblings); 50938c2ecf20Sopenharmony_ci } 50948c2ecf20Sopenharmony_ci 50958c2ecf20Sopenharmony_ci if (rt) 50968c2ecf20Sopenharmony_ci inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 50978c2ecf20Sopenharmony_ci} 50988c2ecf20Sopenharmony_ci 50998c2ecf20Sopenharmony_cistatic bool ip6_route_mpath_should_notify(const struct fib6_info *rt) 51008c2ecf20Sopenharmony_ci{ 51018c2ecf20Sopenharmony_ci bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); 51028c2ecf20Sopenharmony_ci bool should_notify = false; 51038c2ecf20Sopenharmony_ci struct fib6_info *leaf; 51048c2ecf20Sopenharmony_ci struct fib6_node *fn; 51058c2ecf20Sopenharmony_ci 51068c2ecf20Sopenharmony_ci rcu_read_lock(); 51078c2ecf20Sopenharmony_ci fn = rcu_dereference(rt->fib6_node); 51088c2ecf20Sopenharmony_ci if (!fn) 51098c2ecf20Sopenharmony_ci goto out; 51108c2ecf20Sopenharmony_ci 51118c2ecf20Sopenharmony_ci leaf = rcu_dereference(fn->leaf); 51128c2ecf20Sopenharmony_ci if (!leaf) 51138c2ecf20Sopenharmony_ci goto out; 51148c2ecf20Sopenharmony_ci 51158c2ecf20Sopenharmony_ci if (rt == leaf || 51168c2ecf20Sopenharmony_ci (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric && 51178c2ecf20Sopenharmony_ci rt6_qualify_for_ecmp(leaf))) 51188c2ecf20Sopenharmony_ci should_notify = true; 51198c2ecf20Sopenharmony_ciout: 51208c2ecf20Sopenharmony_ci rcu_read_unlock(); 51218c2ecf20Sopenharmony_ci 51228c2ecf20Sopenharmony_ci return should_notify; 51238c2ecf20Sopenharmony_ci} 51248c2ecf20Sopenharmony_ci 51258c2ecf20Sopenharmony_cistatic int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla, 51268c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 51278c2ecf20Sopenharmony_ci{ 51288c2ecf20Sopenharmony_ci if (nla_len(nla) < sizeof(*gw)) { 51298c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY"); 51308c2ecf20Sopenharmony_ci return -EINVAL; 51318c2ecf20Sopenharmony_ci } 51328c2ecf20Sopenharmony_ci 51338c2ecf20Sopenharmony_ci *gw = nla_get_in6_addr(nla); 51348c2ecf20Sopenharmony_ci 51358c2ecf20Sopenharmony_ci return 0; 51368c2ecf20Sopenharmony_ci} 51378c2ecf20Sopenharmony_ci 51388c2ecf20Sopenharmony_cistatic int ip6_route_multipath_add(struct fib6_config *cfg, 51398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 51408c2ecf20Sopenharmony_ci{ 51418c2ecf20Sopenharmony_ci struct fib6_info *rt_notif = NULL, *rt_last = NULL; 51428c2ecf20Sopenharmony_ci struct nl_info *info = &cfg->fc_nlinfo; 51438c2ecf20Sopenharmony_ci struct fib6_config r_cfg; 51448c2ecf20Sopenharmony_ci struct rtnexthop *rtnh; 51458c2ecf20Sopenharmony_ci struct fib6_info *rt; 51468c2ecf20Sopenharmony_ci struct rt6_nh *err_nh; 51478c2ecf20Sopenharmony_ci struct rt6_nh *nh, *nh_safe; 51488c2ecf20Sopenharmony_ci __u16 nlflags; 51498c2ecf20Sopenharmony_ci int remaining; 51508c2ecf20Sopenharmony_ci int attrlen; 51518c2ecf20Sopenharmony_ci int err = 1; 51528c2ecf20Sopenharmony_ci int nhn = 0; 51538c2ecf20Sopenharmony_ci int replace = (cfg->fc_nlinfo.nlh && 51548c2ecf20Sopenharmony_ci (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 51558c2ecf20Sopenharmony_ci LIST_HEAD(rt6_nh_list); 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_ci nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 51588c2ecf20Sopenharmony_ci if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 51598c2ecf20Sopenharmony_ci nlflags |= NLM_F_APPEND; 51608c2ecf20Sopenharmony_ci 51618c2ecf20Sopenharmony_ci remaining = cfg->fc_mp_len; 51628c2ecf20Sopenharmony_ci rtnh = (struct rtnexthop *)cfg->fc_mp; 51638c2ecf20Sopenharmony_ci 51648c2ecf20Sopenharmony_ci /* Parse a Multipath Entry and build a list (rt6_nh_list) of 51658c2ecf20Sopenharmony_ci * fib6_info structs per nexthop 51668c2ecf20Sopenharmony_ci */ 51678c2ecf20Sopenharmony_ci while (rtnh_ok(rtnh, remaining)) { 51688c2ecf20Sopenharmony_ci memcpy(&r_cfg, cfg, sizeof(*cfg)); 51698c2ecf20Sopenharmony_ci if (rtnh->rtnh_ifindex) 51708c2ecf20Sopenharmony_ci r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 51718c2ecf20Sopenharmony_ci 51728c2ecf20Sopenharmony_ci attrlen = rtnh_attrlen(rtnh); 51738c2ecf20Sopenharmony_ci if (attrlen > 0) { 51748c2ecf20Sopenharmony_ci struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_GATEWAY); 51778c2ecf20Sopenharmony_ci if (nla) { 51788c2ecf20Sopenharmony_ci err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla, 51798c2ecf20Sopenharmony_ci extack); 51808c2ecf20Sopenharmony_ci if (err) 51818c2ecf20Sopenharmony_ci goto cleanup; 51828c2ecf20Sopenharmony_ci 51838c2ecf20Sopenharmony_ci r_cfg.fc_flags |= RTF_GATEWAY; 51848c2ecf20Sopenharmony_ci } 51858c2ecf20Sopenharmony_ci r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 51868c2ecf20Sopenharmony_ci 51878c2ecf20Sopenharmony_ci /* RTA_ENCAP_TYPE length checked in 51888c2ecf20Sopenharmony_ci * lwtunnel_valid_encap_type_attr 51898c2ecf20Sopenharmony_ci */ 51908c2ecf20Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 51918c2ecf20Sopenharmony_ci if (nla) 51928c2ecf20Sopenharmony_ci r_cfg.fc_encap_type = nla_get_u16(nla); 51938c2ecf20Sopenharmony_ci } 51948c2ecf20Sopenharmony_ci 51958c2ecf20Sopenharmony_ci r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 51968c2ecf20Sopenharmony_ci rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack); 51978c2ecf20Sopenharmony_ci if (IS_ERR(rt)) { 51988c2ecf20Sopenharmony_ci err = PTR_ERR(rt); 51998c2ecf20Sopenharmony_ci rt = NULL; 52008c2ecf20Sopenharmony_ci goto cleanup; 52018c2ecf20Sopenharmony_ci } 52028c2ecf20Sopenharmony_ci if (!rt6_qualify_for_ecmp(rt)) { 52038c2ecf20Sopenharmony_ci err = -EINVAL; 52048c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 52058c2ecf20Sopenharmony_ci "Device only routes can not be added for IPv6 using the multipath API."); 52068c2ecf20Sopenharmony_ci fib6_info_release(rt); 52078c2ecf20Sopenharmony_ci goto cleanup; 52088c2ecf20Sopenharmony_ci } 52098c2ecf20Sopenharmony_ci 52108c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1; 52118c2ecf20Sopenharmony_ci 52128c2ecf20Sopenharmony_ci err = ip6_route_info_append(info->nl_net, &rt6_nh_list, 52138c2ecf20Sopenharmony_ci rt, &r_cfg); 52148c2ecf20Sopenharmony_ci if (err) { 52158c2ecf20Sopenharmony_ci fib6_info_release(rt); 52168c2ecf20Sopenharmony_ci goto cleanup; 52178c2ecf20Sopenharmony_ci } 52188c2ecf20Sopenharmony_ci 52198c2ecf20Sopenharmony_ci rtnh = rtnh_next(rtnh, &remaining); 52208c2ecf20Sopenharmony_ci } 52218c2ecf20Sopenharmony_ci 52228c2ecf20Sopenharmony_ci if (list_empty(&rt6_nh_list)) { 52238c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 52248c2ecf20Sopenharmony_ci "Invalid nexthop configuration - no valid nexthops"); 52258c2ecf20Sopenharmony_ci return -EINVAL; 52268c2ecf20Sopenharmony_ci } 52278c2ecf20Sopenharmony_ci 52288c2ecf20Sopenharmony_ci /* for add and replace send one notification with all nexthops. 52298c2ecf20Sopenharmony_ci * Skip the notification in fib6_add_rt2node and send one with 52308c2ecf20Sopenharmony_ci * the full route when done 52318c2ecf20Sopenharmony_ci */ 52328c2ecf20Sopenharmony_ci info->skip_notify = 1; 52338c2ecf20Sopenharmony_ci 52348c2ecf20Sopenharmony_ci /* For add and replace, send one notification with all nexthops. For 52358c2ecf20Sopenharmony_ci * append, send one notification with all appended nexthops. 52368c2ecf20Sopenharmony_ci */ 52378c2ecf20Sopenharmony_ci info->skip_notify_kernel = 1; 52388c2ecf20Sopenharmony_ci 52398c2ecf20Sopenharmony_ci err_nh = NULL; 52408c2ecf20Sopenharmony_ci list_for_each_entry(nh, &rt6_nh_list, next) { 52418c2ecf20Sopenharmony_ci err = __ip6_ins_rt(nh->fib6_info, info, extack); 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_ci if (err) { 52448c2ecf20Sopenharmony_ci if (replace && nhn) 52458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 52468c2ecf20Sopenharmony_ci "multipath route replace failed (check consistency of installed routes)"); 52478c2ecf20Sopenharmony_ci err_nh = nh; 52488c2ecf20Sopenharmony_ci goto add_errout; 52498c2ecf20Sopenharmony_ci } 52508c2ecf20Sopenharmony_ci /* save reference to last route successfully inserted */ 52518c2ecf20Sopenharmony_ci rt_last = nh->fib6_info; 52528c2ecf20Sopenharmony_ci 52538c2ecf20Sopenharmony_ci /* save reference to first route for notification */ 52548c2ecf20Sopenharmony_ci if (!rt_notif) 52558c2ecf20Sopenharmony_ci rt_notif = nh->fib6_info; 52568c2ecf20Sopenharmony_ci 52578c2ecf20Sopenharmony_ci /* Because each route is added like a single route we remove 52588c2ecf20Sopenharmony_ci * these flags after the first nexthop: if there is a collision, 52598c2ecf20Sopenharmony_ci * we have already failed to add the first nexthop: 52608c2ecf20Sopenharmony_ci * fib6_add_rt2node() has rejected it; when replacing, old 52618c2ecf20Sopenharmony_ci * nexthops have been replaced by first new, the rest should 52628c2ecf20Sopenharmony_ci * be added to it. 52638c2ecf20Sopenharmony_ci */ 52648c2ecf20Sopenharmony_ci if (cfg->fc_nlinfo.nlh) { 52658c2ecf20Sopenharmony_ci cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 52668c2ecf20Sopenharmony_ci NLM_F_REPLACE); 52678c2ecf20Sopenharmony_ci cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE; 52688c2ecf20Sopenharmony_ci } 52698c2ecf20Sopenharmony_ci nhn++; 52708c2ecf20Sopenharmony_ci } 52718c2ecf20Sopenharmony_ci 52728c2ecf20Sopenharmony_ci /* An in-kernel notification should only be sent in case the new 52738c2ecf20Sopenharmony_ci * multipath route is added as the first route in the node, or if 52748c2ecf20Sopenharmony_ci * it was appended to it. We pass 'rt_notif' since it is the first 52758c2ecf20Sopenharmony_ci * sibling and might allow us to skip some checks in the replace case. 52768c2ecf20Sopenharmony_ci */ 52778c2ecf20Sopenharmony_ci if (ip6_route_mpath_should_notify(rt_notif)) { 52788c2ecf20Sopenharmony_ci enum fib_event_type fib_event; 52798c2ecf20Sopenharmony_ci 52808c2ecf20Sopenharmony_ci if (rt_notif->fib6_nsiblings != nhn - 1) 52818c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_APPEND; 52828c2ecf20Sopenharmony_ci else 52838c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 52848c2ecf20Sopenharmony_ci 52858c2ecf20Sopenharmony_ci err = call_fib6_multipath_entry_notifiers(info->nl_net, 52868c2ecf20Sopenharmony_ci fib_event, rt_notif, 52878c2ecf20Sopenharmony_ci nhn - 1, extack); 52888c2ecf20Sopenharmony_ci if (err) { 52898c2ecf20Sopenharmony_ci /* Delete all the siblings that were just added */ 52908c2ecf20Sopenharmony_ci err_nh = NULL; 52918c2ecf20Sopenharmony_ci goto add_errout; 52928c2ecf20Sopenharmony_ci } 52938c2ecf20Sopenharmony_ci } 52948c2ecf20Sopenharmony_ci 52958c2ecf20Sopenharmony_ci /* success ... tell user about new route */ 52968c2ecf20Sopenharmony_ci ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 52978c2ecf20Sopenharmony_ci goto cleanup; 52988c2ecf20Sopenharmony_ci 52998c2ecf20Sopenharmony_ciadd_errout: 53008c2ecf20Sopenharmony_ci /* send notification for routes that were added so that 53018c2ecf20Sopenharmony_ci * the delete notifications sent by ip6_route_del are 53028c2ecf20Sopenharmony_ci * coherent 53038c2ecf20Sopenharmony_ci */ 53048c2ecf20Sopenharmony_ci if (rt_notif) 53058c2ecf20Sopenharmony_ci ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 53068c2ecf20Sopenharmony_ci 53078c2ecf20Sopenharmony_ci /* Delete routes that were already added */ 53088c2ecf20Sopenharmony_ci list_for_each_entry(nh, &rt6_nh_list, next) { 53098c2ecf20Sopenharmony_ci if (err_nh == nh) 53108c2ecf20Sopenharmony_ci break; 53118c2ecf20Sopenharmony_ci ip6_route_del(&nh->r_cfg, extack); 53128c2ecf20Sopenharmony_ci } 53138c2ecf20Sopenharmony_ci 53148c2ecf20Sopenharmony_cicleanup: 53158c2ecf20Sopenharmony_ci list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 53168c2ecf20Sopenharmony_ci fib6_info_release(nh->fib6_info); 53178c2ecf20Sopenharmony_ci list_del(&nh->next); 53188c2ecf20Sopenharmony_ci kfree(nh); 53198c2ecf20Sopenharmony_ci } 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_ci return err; 53228c2ecf20Sopenharmony_ci} 53238c2ecf20Sopenharmony_ci 53248c2ecf20Sopenharmony_cistatic int ip6_route_multipath_del(struct fib6_config *cfg, 53258c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 53268c2ecf20Sopenharmony_ci{ 53278c2ecf20Sopenharmony_ci struct fib6_config r_cfg; 53288c2ecf20Sopenharmony_ci struct rtnexthop *rtnh; 53298c2ecf20Sopenharmony_ci int last_err = 0; 53308c2ecf20Sopenharmony_ci int remaining; 53318c2ecf20Sopenharmony_ci int attrlen; 53328c2ecf20Sopenharmony_ci int err; 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_ci remaining = cfg->fc_mp_len; 53358c2ecf20Sopenharmony_ci rtnh = (struct rtnexthop *)cfg->fc_mp; 53368c2ecf20Sopenharmony_ci 53378c2ecf20Sopenharmony_ci /* Parse a Multipath Entry */ 53388c2ecf20Sopenharmony_ci while (rtnh_ok(rtnh, remaining)) { 53398c2ecf20Sopenharmony_ci memcpy(&r_cfg, cfg, sizeof(*cfg)); 53408c2ecf20Sopenharmony_ci if (rtnh->rtnh_ifindex) 53418c2ecf20Sopenharmony_ci r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 53428c2ecf20Sopenharmony_ci 53438c2ecf20Sopenharmony_ci attrlen = rtnh_attrlen(rtnh); 53448c2ecf20Sopenharmony_ci if (attrlen > 0) { 53458c2ecf20Sopenharmony_ci struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 53468c2ecf20Sopenharmony_ci 53478c2ecf20Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_GATEWAY); 53488c2ecf20Sopenharmony_ci if (nla) { 53498c2ecf20Sopenharmony_ci err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla, 53508c2ecf20Sopenharmony_ci extack); 53518c2ecf20Sopenharmony_ci if (err) { 53528c2ecf20Sopenharmony_ci last_err = err; 53538c2ecf20Sopenharmony_ci goto next_rtnh; 53548c2ecf20Sopenharmony_ci } 53558c2ecf20Sopenharmony_ci 53568c2ecf20Sopenharmony_ci r_cfg.fc_flags |= RTF_GATEWAY; 53578c2ecf20Sopenharmony_ci } 53588c2ecf20Sopenharmony_ci } 53598c2ecf20Sopenharmony_ci err = ip6_route_del(&r_cfg, extack); 53608c2ecf20Sopenharmony_ci if (err) 53618c2ecf20Sopenharmony_ci last_err = err; 53628c2ecf20Sopenharmony_ci 53638c2ecf20Sopenharmony_cinext_rtnh: 53648c2ecf20Sopenharmony_ci rtnh = rtnh_next(rtnh, &remaining); 53658c2ecf20Sopenharmony_ci } 53668c2ecf20Sopenharmony_ci 53678c2ecf20Sopenharmony_ci return last_err; 53688c2ecf20Sopenharmony_ci} 53698c2ecf20Sopenharmony_ci 53708c2ecf20Sopenharmony_cistatic int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 53718c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 53728c2ecf20Sopenharmony_ci{ 53738c2ecf20Sopenharmony_ci struct fib6_config cfg; 53748c2ecf20Sopenharmony_ci int err; 53758c2ecf20Sopenharmony_ci 53768c2ecf20Sopenharmony_ci err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 53778c2ecf20Sopenharmony_ci if (err < 0) 53788c2ecf20Sopenharmony_ci return err; 53798c2ecf20Sopenharmony_ci 53808c2ecf20Sopenharmony_ci if (cfg.fc_nh_id && 53818c2ecf20Sopenharmony_ci !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) { 53828c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 53838c2ecf20Sopenharmony_ci return -EINVAL; 53848c2ecf20Sopenharmony_ci } 53858c2ecf20Sopenharmony_ci 53868c2ecf20Sopenharmony_ci if (cfg.fc_mp) 53878c2ecf20Sopenharmony_ci return ip6_route_multipath_del(&cfg, extack); 53888c2ecf20Sopenharmony_ci else { 53898c2ecf20Sopenharmony_ci cfg.fc_delete_all_nh = 1; 53908c2ecf20Sopenharmony_ci return ip6_route_del(&cfg, extack); 53918c2ecf20Sopenharmony_ci } 53928c2ecf20Sopenharmony_ci} 53938c2ecf20Sopenharmony_ci 53948c2ecf20Sopenharmony_cistatic int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 53958c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 53968c2ecf20Sopenharmony_ci{ 53978c2ecf20Sopenharmony_ci struct fib6_config cfg; 53988c2ecf20Sopenharmony_ci int err; 53998c2ecf20Sopenharmony_ci 54008c2ecf20Sopenharmony_ci err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 54018c2ecf20Sopenharmony_ci if (err < 0) 54028c2ecf20Sopenharmony_ci return err; 54038c2ecf20Sopenharmony_ci 54048c2ecf20Sopenharmony_ci if (cfg.fc_metric == 0) 54058c2ecf20Sopenharmony_ci cfg.fc_metric = IP6_RT_PRIO_USER; 54068c2ecf20Sopenharmony_ci 54078c2ecf20Sopenharmony_ci if (cfg.fc_mp) 54088c2ecf20Sopenharmony_ci return ip6_route_multipath_add(&cfg, extack); 54098c2ecf20Sopenharmony_ci else 54108c2ecf20Sopenharmony_ci return ip6_route_add(&cfg, GFP_KERNEL, extack); 54118c2ecf20Sopenharmony_ci} 54128c2ecf20Sopenharmony_ci 54138c2ecf20Sopenharmony_ci/* add the overhead of this fib6_nh to nexthop_len */ 54148c2ecf20Sopenharmony_cistatic int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg) 54158c2ecf20Sopenharmony_ci{ 54168c2ecf20Sopenharmony_ci int *nexthop_len = arg; 54178c2ecf20Sopenharmony_ci 54188c2ecf20Sopenharmony_ci *nexthop_len += nla_total_size(0) /* RTA_MULTIPATH */ 54198c2ecf20Sopenharmony_ci + NLA_ALIGN(sizeof(struct rtnexthop)) 54208c2ecf20Sopenharmony_ci + nla_total_size(16); /* RTA_GATEWAY */ 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_ci if (nh->fib_nh_lws) { 54238c2ecf20Sopenharmony_ci /* RTA_ENCAP_TYPE */ 54248c2ecf20Sopenharmony_ci *nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws); 54258c2ecf20Sopenharmony_ci /* RTA_ENCAP */ 54268c2ecf20Sopenharmony_ci *nexthop_len += nla_total_size(2); 54278c2ecf20Sopenharmony_ci } 54288c2ecf20Sopenharmony_ci 54298c2ecf20Sopenharmony_ci return 0; 54308c2ecf20Sopenharmony_ci} 54318c2ecf20Sopenharmony_ci 54328c2ecf20Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i) 54338c2ecf20Sopenharmony_ci{ 54348c2ecf20Sopenharmony_ci int nexthop_len; 54358c2ecf20Sopenharmony_ci 54368c2ecf20Sopenharmony_ci if (f6i->nh) { 54378c2ecf20Sopenharmony_ci nexthop_len = nla_total_size(4); /* RTA_NH_ID */ 54388c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size, 54398c2ecf20Sopenharmony_ci &nexthop_len); 54408c2ecf20Sopenharmony_ci } else { 54418c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 54428c2ecf20Sopenharmony_ci struct fib6_nh *nh = f6i->fib6_nh; 54438c2ecf20Sopenharmony_ci 54448c2ecf20Sopenharmony_ci nexthop_len = 0; 54458c2ecf20Sopenharmony_ci if (f6i->fib6_nsiblings) { 54468c2ecf20Sopenharmony_ci rt6_nh_nlmsg_size(nh, &nexthop_len); 54478c2ecf20Sopenharmony_ci 54488c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 54498c2ecf20Sopenharmony_ci &f6i->fib6_siblings, fib6_siblings) { 54508c2ecf20Sopenharmony_ci rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len); 54518c2ecf20Sopenharmony_ci } 54528c2ecf20Sopenharmony_ci } 54538c2ecf20Sopenharmony_ci nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws); 54548c2ecf20Sopenharmony_ci } 54558c2ecf20Sopenharmony_ci 54568c2ecf20Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct rtmsg)) 54578c2ecf20Sopenharmony_ci + nla_total_size(16) /* RTA_SRC */ 54588c2ecf20Sopenharmony_ci + nla_total_size(16) /* RTA_DST */ 54598c2ecf20Sopenharmony_ci + nla_total_size(16) /* RTA_GATEWAY */ 54608c2ecf20Sopenharmony_ci + nla_total_size(16) /* RTA_PREFSRC */ 54618c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_TABLE */ 54628c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_IIF */ 54638c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_OIF */ 54648c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_PRIORITY */ 54658c2ecf20Sopenharmony_ci + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 54668c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct rta_cacheinfo)) 54678c2ecf20Sopenharmony_ci + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 54688c2ecf20Sopenharmony_ci + nla_total_size(1) /* RTA_PREF */ 54698c2ecf20Sopenharmony_ci + nexthop_len; 54708c2ecf20Sopenharmony_ci} 54718c2ecf20Sopenharmony_ci 54728c2ecf20Sopenharmony_cistatic int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh, 54738c2ecf20Sopenharmony_ci unsigned char *flags) 54748c2ecf20Sopenharmony_ci{ 54758c2ecf20Sopenharmony_ci if (nexthop_is_multipath(nh)) { 54768c2ecf20Sopenharmony_ci struct nlattr *mp; 54778c2ecf20Sopenharmony_ci 54788c2ecf20Sopenharmony_ci mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 54798c2ecf20Sopenharmony_ci if (!mp) 54808c2ecf20Sopenharmony_ci goto nla_put_failure; 54818c2ecf20Sopenharmony_ci 54828c2ecf20Sopenharmony_ci if (nexthop_mpath_fill_node(skb, nh, AF_INET6)) 54838c2ecf20Sopenharmony_ci goto nla_put_failure; 54848c2ecf20Sopenharmony_ci 54858c2ecf20Sopenharmony_ci nla_nest_end(skb, mp); 54868c2ecf20Sopenharmony_ci } else { 54878c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh; 54888c2ecf20Sopenharmony_ci 54898c2ecf20Sopenharmony_ci fib6_nh = nexthop_fib6_nh(nh); 54908c2ecf20Sopenharmony_ci if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6, 54918c2ecf20Sopenharmony_ci flags, false) < 0) 54928c2ecf20Sopenharmony_ci goto nla_put_failure; 54938c2ecf20Sopenharmony_ci } 54948c2ecf20Sopenharmony_ci 54958c2ecf20Sopenharmony_ci return 0; 54968c2ecf20Sopenharmony_ci 54978c2ecf20Sopenharmony_cinla_put_failure: 54988c2ecf20Sopenharmony_ci return -EMSGSIZE; 54998c2ecf20Sopenharmony_ci} 55008c2ecf20Sopenharmony_ci 55018c2ecf20Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb, 55028c2ecf20Sopenharmony_ci struct fib6_info *rt, struct dst_entry *dst, 55038c2ecf20Sopenharmony_ci struct in6_addr *dest, struct in6_addr *src, 55048c2ecf20Sopenharmony_ci int iif, int type, u32 portid, u32 seq, 55058c2ecf20Sopenharmony_ci unsigned int flags) 55068c2ecf20Sopenharmony_ci{ 55078c2ecf20Sopenharmony_ci struct rt6_info *rt6 = (struct rt6_info *)dst; 55088c2ecf20Sopenharmony_ci struct rt6key *rt6_dst, *rt6_src; 55098c2ecf20Sopenharmony_ci u32 *pmetrics, table, rt6_flags; 55108c2ecf20Sopenharmony_ci unsigned char nh_flags = 0; 55118c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 55128c2ecf20Sopenharmony_ci struct rtmsg *rtm; 55138c2ecf20Sopenharmony_ci long expires = 0; 55148c2ecf20Sopenharmony_ci 55158c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 55168c2ecf20Sopenharmony_ci if (!nlh) 55178c2ecf20Sopenharmony_ci return -EMSGSIZE; 55188c2ecf20Sopenharmony_ci 55198c2ecf20Sopenharmony_ci if (rt6) { 55208c2ecf20Sopenharmony_ci rt6_dst = &rt6->rt6i_dst; 55218c2ecf20Sopenharmony_ci rt6_src = &rt6->rt6i_src; 55228c2ecf20Sopenharmony_ci rt6_flags = rt6->rt6i_flags; 55238c2ecf20Sopenharmony_ci } else { 55248c2ecf20Sopenharmony_ci rt6_dst = &rt->fib6_dst; 55258c2ecf20Sopenharmony_ci rt6_src = &rt->fib6_src; 55268c2ecf20Sopenharmony_ci rt6_flags = rt->fib6_flags; 55278c2ecf20Sopenharmony_ci } 55288c2ecf20Sopenharmony_ci 55298c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 55308c2ecf20Sopenharmony_ci rtm->rtm_family = AF_INET6; 55318c2ecf20Sopenharmony_ci rtm->rtm_dst_len = rt6_dst->plen; 55328c2ecf20Sopenharmony_ci rtm->rtm_src_len = rt6_src->plen; 55338c2ecf20Sopenharmony_ci rtm->rtm_tos = 0; 55348c2ecf20Sopenharmony_ci if (rt->fib6_table) 55358c2ecf20Sopenharmony_ci table = rt->fib6_table->tb6_id; 55368c2ecf20Sopenharmony_ci else 55378c2ecf20Sopenharmony_ci table = RT6_TABLE_UNSPEC; 55388c2ecf20Sopenharmony_ci rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT; 55398c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_TABLE, table)) 55408c2ecf20Sopenharmony_ci goto nla_put_failure; 55418c2ecf20Sopenharmony_ci 55428c2ecf20Sopenharmony_ci rtm->rtm_type = rt->fib6_type; 55438c2ecf20Sopenharmony_ci rtm->rtm_flags = 0; 55448c2ecf20Sopenharmony_ci rtm->rtm_scope = RT_SCOPE_UNIVERSE; 55458c2ecf20Sopenharmony_ci rtm->rtm_protocol = rt->fib6_protocol; 55468c2ecf20Sopenharmony_ci 55478c2ecf20Sopenharmony_ci if (rt6_flags & RTF_CACHE) 55488c2ecf20Sopenharmony_ci rtm->rtm_flags |= RTM_F_CLONED; 55498c2ecf20Sopenharmony_ci 55508c2ecf20Sopenharmony_ci if (dest) { 55518c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_DST, dest)) 55528c2ecf20Sopenharmony_ci goto nla_put_failure; 55538c2ecf20Sopenharmony_ci rtm->rtm_dst_len = 128; 55548c2ecf20Sopenharmony_ci } else if (rtm->rtm_dst_len) 55558c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr)) 55568c2ecf20Sopenharmony_ci goto nla_put_failure; 55578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 55588c2ecf20Sopenharmony_ci if (src) { 55598c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_SRC, src)) 55608c2ecf20Sopenharmony_ci goto nla_put_failure; 55618c2ecf20Sopenharmony_ci rtm->rtm_src_len = 128; 55628c2ecf20Sopenharmony_ci } else if (rtm->rtm_src_len && 55638c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr)) 55648c2ecf20Sopenharmony_ci goto nla_put_failure; 55658c2ecf20Sopenharmony_ci#endif 55668c2ecf20Sopenharmony_ci if (iif) { 55678c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 55688c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(&rt6_dst->addr)) { 55698c2ecf20Sopenharmony_ci int err = ip6mr_get_route(net, skb, rtm, portid); 55708c2ecf20Sopenharmony_ci 55718c2ecf20Sopenharmony_ci if (err == 0) 55728c2ecf20Sopenharmony_ci return 0; 55738c2ecf20Sopenharmony_ci if (err < 0) 55748c2ecf20Sopenharmony_ci goto nla_put_failure; 55758c2ecf20Sopenharmony_ci } else 55768c2ecf20Sopenharmony_ci#endif 55778c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_IIF, iif)) 55788c2ecf20Sopenharmony_ci goto nla_put_failure; 55798c2ecf20Sopenharmony_ci } else if (dest) { 55808c2ecf20Sopenharmony_ci struct in6_addr saddr_buf; 55818c2ecf20Sopenharmony_ci if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 && 55828c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 55838c2ecf20Sopenharmony_ci goto nla_put_failure; 55848c2ecf20Sopenharmony_ci } 55858c2ecf20Sopenharmony_ci 55868c2ecf20Sopenharmony_ci if (rt->fib6_prefsrc.plen) { 55878c2ecf20Sopenharmony_ci struct in6_addr saddr_buf; 55888c2ecf20Sopenharmony_ci saddr_buf = rt->fib6_prefsrc.addr; 55898c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 55908c2ecf20Sopenharmony_ci goto nla_put_failure; 55918c2ecf20Sopenharmony_ci } 55928c2ecf20Sopenharmony_ci 55938c2ecf20Sopenharmony_ci pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics; 55948c2ecf20Sopenharmony_ci if (rtnetlink_put_metrics(skb, pmetrics) < 0) 55958c2ecf20Sopenharmony_ci goto nla_put_failure; 55968c2ecf20Sopenharmony_ci 55978c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric)) 55988c2ecf20Sopenharmony_ci goto nla_put_failure; 55998c2ecf20Sopenharmony_ci 56008c2ecf20Sopenharmony_ci /* For multipath routes, walk the siblings list and add 56018c2ecf20Sopenharmony_ci * each as a nexthop within RTA_MULTIPATH. 56028c2ecf20Sopenharmony_ci */ 56038c2ecf20Sopenharmony_ci if (rt6) { 56048c2ecf20Sopenharmony_ci if (rt6_flags & RTF_GATEWAY && 56058c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway)) 56068c2ecf20Sopenharmony_ci goto nla_put_failure; 56078c2ecf20Sopenharmony_ci 56088c2ecf20Sopenharmony_ci if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex)) 56098c2ecf20Sopenharmony_ci goto nla_put_failure; 56108c2ecf20Sopenharmony_ci } else if (rt->fib6_nsiblings) { 56118c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 56128c2ecf20Sopenharmony_ci struct nlattr *mp; 56138c2ecf20Sopenharmony_ci 56148c2ecf20Sopenharmony_ci mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 56158c2ecf20Sopenharmony_ci if (!mp) 56168c2ecf20Sopenharmony_ci goto nla_put_failure; 56178c2ecf20Sopenharmony_ci 56188c2ecf20Sopenharmony_ci if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common, 56198c2ecf20Sopenharmony_ci rt->fib6_nh->fib_nh_weight, AF_INET6, 56208c2ecf20Sopenharmony_ci 0) < 0) 56218c2ecf20Sopenharmony_ci goto nla_put_failure; 56228c2ecf20Sopenharmony_ci 56238c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 56248c2ecf20Sopenharmony_ci &rt->fib6_siblings, fib6_siblings) { 56258c2ecf20Sopenharmony_ci if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common, 56268c2ecf20Sopenharmony_ci sibling->fib6_nh->fib_nh_weight, 56278c2ecf20Sopenharmony_ci AF_INET6, 0) < 0) 56288c2ecf20Sopenharmony_ci goto nla_put_failure; 56298c2ecf20Sopenharmony_ci } 56308c2ecf20Sopenharmony_ci 56318c2ecf20Sopenharmony_ci nla_nest_end(skb, mp); 56328c2ecf20Sopenharmony_ci } else if (rt->nh) { 56338c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id)) 56348c2ecf20Sopenharmony_ci goto nla_put_failure; 56358c2ecf20Sopenharmony_ci 56368c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(rt->nh)) 56378c2ecf20Sopenharmony_ci rtm->rtm_type = RTN_BLACKHOLE; 56388c2ecf20Sopenharmony_ci 56398c2ecf20Sopenharmony_ci if (READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode) && 56408c2ecf20Sopenharmony_ci rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0) 56418c2ecf20Sopenharmony_ci goto nla_put_failure; 56428c2ecf20Sopenharmony_ci 56438c2ecf20Sopenharmony_ci rtm->rtm_flags |= nh_flags; 56448c2ecf20Sopenharmony_ci } else { 56458c2ecf20Sopenharmony_ci if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6, 56468c2ecf20Sopenharmony_ci &nh_flags, false) < 0) 56478c2ecf20Sopenharmony_ci goto nla_put_failure; 56488c2ecf20Sopenharmony_ci 56498c2ecf20Sopenharmony_ci rtm->rtm_flags |= nh_flags; 56508c2ecf20Sopenharmony_ci } 56518c2ecf20Sopenharmony_ci 56528c2ecf20Sopenharmony_ci if (rt6_flags & RTF_EXPIRES) { 56538c2ecf20Sopenharmony_ci expires = dst ? dst->expires : rt->expires; 56548c2ecf20Sopenharmony_ci expires -= jiffies; 56558c2ecf20Sopenharmony_ci } 56568c2ecf20Sopenharmony_ci 56578c2ecf20Sopenharmony_ci if (!dst) { 56588c2ecf20Sopenharmony_ci if (rt->offload) 56598c2ecf20Sopenharmony_ci rtm->rtm_flags |= RTM_F_OFFLOAD; 56608c2ecf20Sopenharmony_ci if (rt->trap) 56618c2ecf20Sopenharmony_ci rtm->rtm_flags |= RTM_F_TRAP; 56628c2ecf20Sopenharmony_ci } 56638c2ecf20Sopenharmony_ci 56648c2ecf20Sopenharmony_ci if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0) 56658c2ecf20Sopenharmony_ci goto nla_put_failure; 56668c2ecf20Sopenharmony_ci 56678c2ecf20Sopenharmony_ci if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags))) 56688c2ecf20Sopenharmony_ci goto nla_put_failure; 56698c2ecf20Sopenharmony_ci 56708c2ecf20Sopenharmony_ci 56718c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 56728c2ecf20Sopenharmony_ci return 0; 56738c2ecf20Sopenharmony_ci 56748c2ecf20Sopenharmony_cinla_put_failure: 56758c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 56768c2ecf20Sopenharmony_ci return -EMSGSIZE; 56778c2ecf20Sopenharmony_ci} 56788c2ecf20Sopenharmony_ci 56798c2ecf20Sopenharmony_cistatic int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg) 56808c2ecf20Sopenharmony_ci{ 56818c2ecf20Sopenharmony_ci const struct net_device *dev = arg; 56828c2ecf20Sopenharmony_ci 56838c2ecf20Sopenharmony_ci if (nh->fib_nh_dev == dev) 56848c2ecf20Sopenharmony_ci return 1; 56858c2ecf20Sopenharmony_ci 56868c2ecf20Sopenharmony_ci return 0; 56878c2ecf20Sopenharmony_ci} 56888c2ecf20Sopenharmony_ci 56898c2ecf20Sopenharmony_cistatic bool fib6_info_uses_dev(const struct fib6_info *f6i, 56908c2ecf20Sopenharmony_ci const struct net_device *dev) 56918c2ecf20Sopenharmony_ci{ 56928c2ecf20Sopenharmony_ci if (f6i->nh) { 56938c2ecf20Sopenharmony_ci struct net_device *_dev = (struct net_device *)dev; 56948c2ecf20Sopenharmony_ci 56958c2ecf20Sopenharmony_ci return !!nexthop_for_each_fib6_nh(f6i->nh, 56968c2ecf20Sopenharmony_ci fib6_info_nh_uses_dev, 56978c2ecf20Sopenharmony_ci _dev); 56988c2ecf20Sopenharmony_ci } 56998c2ecf20Sopenharmony_ci 57008c2ecf20Sopenharmony_ci if (f6i->fib6_nh->fib_nh_dev == dev) 57018c2ecf20Sopenharmony_ci return true; 57028c2ecf20Sopenharmony_ci 57038c2ecf20Sopenharmony_ci if (f6i->fib6_nsiblings) { 57048c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 57058c2ecf20Sopenharmony_ci 57068c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 57078c2ecf20Sopenharmony_ci &f6i->fib6_siblings, fib6_siblings) { 57088c2ecf20Sopenharmony_ci if (sibling->fib6_nh->fib_nh_dev == dev) 57098c2ecf20Sopenharmony_ci return true; 57108c2ecf20Sopenharmony_ci } 57118c2ecf20Sopenharmony_ci } 57128c2ecf20Sopenharmony_ci 57138c2ecf20Sopenharmony_ci return false; 57148c2ecf20Sopenharmony_ci} 57158c2ecf20Sopenharmony_ci 57168c2ecf20Sopenharmony_cistruct fib6_nh_exception_dump_walker { 57178c2ecf20Sopenharmony_ci struct rt6_rtnl_dump_arg *dump; 57188c2ecf20Sopenharmony_ci struct fib6_info *rt; 57198c2ecf20Sopenharmony_ci unsigned int flags; 57208c2ecf20Sopenharmony_ci unsigned int skip; 57218c2ecf20Sopenharmony_ci unsigned int count; 57228c2ecf20Sopenharmony_ci}; 57238c2ecf20Sopenharmony_ci 57248c2ecf20Sopenharmony_cistatic int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg) 57258c2ecf20Sopenharmony_ci{ 57268c2ecf20Sopenharmony_ci struct fib6_nh_exception_dump_walker *w = arg; 57278c2ecf20Sopenharmony_ci struct rt6_rtnl_dump_arg *dump = w->dump; 57288c2ecf20Sopenharmony_ci struct rt6_exception_bucket *bucket; 57298c2ecf20Sopenharmony_ci struct rt6_exception *rt6_ex; 57308c2ecf20Sopenharmony_ci int i, err; 57318c2ecf20Sopenharmony_ci 57328c2ecf20Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, NULL); 57338c2ecf20Sopenharmony_ci if (!bucket) 57348c2ecf20Sopenharmony_ci return 0; 57358c2ecf20Sopenharmony_ci 57368c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 57378c2ecf20Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 57388c2ecf20Sopenharmony_ci if (w->skip) { 57398c2ecf20Sopenharmony_ci w->skip--; 57408c2ecf20Sopenharmony_ci continue; 57418c2ecf20Sopenharmony_ci } 57428c2ecf20Sopenharmony_ci 57438c2ecf20Sopenharmony_ci /* Expiration of entries doesn't bump sernum, insertion 57448c2ecf20Sopenharmony_ci * does. Removal is triggered by insertion, so we can 57458c2ecf20Sopenharmony_ci * rely on the fact that if entries change between two 57468c2ecf20Sopenharmony_ci * partial dumps, this node is scanned again completely, 57478c2ecf20Sopenharmony_ci * see rt6_insert_exception() and fib6_dump_table(). 57488c2ecf20Sopenharmony_ci * 57498c2ecf20Sopenharmony_ci * Count expired entries we go through as handled 57508c2ecf20Sopenharmony_ci * entries that we'll skip next time, in case of partial 57518c2ecf20Sopenharmony_ci * node dump. Otherwise, if entries expire meanwhile, 57528c2ecf20Sopenharmony_ci * we'll skip the wrong amount. 57538c2ecf20Sopenharmony_ci */ 57548c2ecf20Sopenharmony_ci if (rt6_check_expired(rt6_ex->rt6i)) { 57558c2ecf20Sopenharmony_ci w->count++; 57568c2ecf20Sopenharmony_ci continue; 57578c2ecf20Sopenharmony_ci } 57588c2ecf20Sopenharmony_ci 57598c2ecf20Sopenharmony_ci err = rt6_fill_node(dump->net, dump->skb, w->rt, 57608c2ecf20Sopenharmony_ci &rt6_ex->rt6i->dst, NULL, NULL, 0, 57618c2ecf20Sopenharmony_ci RTM_NEWROUTE, 57628c2ecf20Sopenharmony_ci NETLINK_CB(dump->cb->skb).portid, 57638c2ecf20Sopenharmony_ci dump->cb->nlh->nlmsg_seq, w->flags); 57648c2ecf20Sopenharmony_ci if (err) 57658c2ecf20Sopenharmony_ci return err; 57668c2ecf20Sopenharmony_ci 57678c2ecf20Sopenharmony_ci w->count++; 57688c2ecf20Sopenharmony_ci } 57698c2ecf20Sopenharmony_ci bucket++; 57708c2ecf20Sopenharmony_ci } 57718c2ecf20Sopenharmony_ci 57728c2ecf20Sopenharmony_ci return 0; 57738c2ecf20Sopenharmony_ci} 57748c2ecf20Sopenharmony_ci 57758c2ecf20Sopenharmony_ci/* Return -1 if done with node, number of handled routes on partial dump */ 57768c2ecf20Sopenharmony_ciint rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip) 57778c2ecf20Sopenharmony_ci{ 57788c2ecf20Sopenharmony_ci struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 57798c2ecf20Sopenharmony_ci struct fib_dump_filter *filter = &arg->filter; 57808c2ecf20Sopenharmony_ci unsigned int flags = NLM_F_MULTI; 57818c2ecf20Sopenharmony_ci struct net *net = arg->net; 57828c2ecf20Sopenharmony_ci int count = 0; 57838c2ecf20Sopenharmony_ci 57848c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) 57858c2ecf20Sopenharmony_ci return -1; 57868c2ecf20Sopenharmony_ci 57878c2ecf20Sopenharmony_ci if ((filter->flags & RTM_F_PREFIX) && 57888c2ecf20Sopenharmony_ci !(rt->fib6_flags & RTF_PREFIX_RT)) { 57898c2ecf20Sopenharmony_ci /* success since this is not a prefix route */ 57908c2ecf20Sopenharmony_ci return -1; 57918c2ecf20Sopenharmony_ci } 57928c2ecf20Sopenharmony_ci if (filter->filter_set && 57938c2ecf20Sopenharmony_ci ((filter->rt_type && rt->fib6_type != filter->rt_type) || 57948c2ecf20Sopenharmony_ci (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) || 57958c2ecf20Sopenharmony_ci (filter->protocol && rt->fib6_protocol != filter->protocol))) { 57968c2ecf20Sopenharmony_ci return -1; 57978c2ecf20Sopenharmony_ci } 57988c2ecf20Sopenharmony_ci 57998c2ecf20Sopenharmony_ci if (filter->filter_set || 58008c2ecf20Sopenharmony_ci !filter->dump_routes || !filter->dump_exceptions) { 58018c2ecf20Sopenharmony_ci flags |= NLM_F_DUMP_FILTERED; 58028c2ecf20Sopenharmony_ci } 58038c2ecf20Sopenharmony_ci 58048c2ecf20Sopenharmony_ci if (filter->dump_routes) { 58058c2ecf20Sopenharmony_ci if (skip) { 58068c2ecf20Sopenharmony_ci skip--; 58078c2ecf20Sopenharmony_ci } else { 58088c2ecf20Sopenharmony_ci if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 58098c2ecf20Sopenharmony_ci 0, RTM_NEWROUTE, 58108c2ecf20Sopenharmony_ci NETLINK_CB(arg->cb->skb).portid, 58118c2ecf20Sopenharmony_ci arg->cb->nlh->nlmsg_seq, flags)) { 58128c2ecf20Sopenharmony_ci return 0; 58138c2ecf20Sopenharmony_ci } 58148c2ecf20Sopenharmony_ci count++; 58158c2ecf20Sopenharmony_ci } 58168c2ecf20Sopenharmony_ci } 58178c2ecf20Sopenharmony_ci 58188c2ecf20Sopenharmony_ci if (filter->dump_exceptions) { 58198c2ecf20Sopenharmony_ci struct fib6_nh_exception_dump_walker w = { .dump = arg, 58208c2ecf20Sopenharmony_ci .rt = rt, 58218c2ecf20Sopenharmony_ci .flags = flags, 58228c2ecf20Sopenharmony_ci .skip = skip, 58238c2ecf20Sopenharmony_ci .count = 0 }; 58248c2ecf20Sopenharmony_ci int err; 58258c2ecf20Sopenharmony_ci 58268c2ecf20Sopenharmony_ci rcu_read_lock(); 58278c2ecf20Sopenharmony_ci if (rt->nh) { 58288c2ecf20Sopenharmony_ci err = nexthop_for_each_fib6_nh(rt->nh, 58298c2ecf20Sopenharmony_ci rt6_nh_dump_exceptions, 58308c2ecf20Sopenharmony_ci &w); 58318c2ecf20Sopenharmony_ci } else { 58328c2ecf20Sopenharmony_ci err = rt6_nh_dump_exceptions(rt->fib6_nh, &w); 58338c2ecf20Sopenharmony_ci } 58348c2ecf20Sopenharmony_ci rcu_read_unlock(); 58358c2ecf20Sopenharmony_ci 58368c2ecf20Sopenharmony_ci if (err) 58378c2ecf20Sopenharmony_ci return count += w.count; 58388c2ecf20Sopenharmony_ci } 58398c2ecf20Sopenharmony_ci 58408c2ecf20Sopenharmony_ci return -1; 58418c2ecf20Sopenharmony_ci} 58428c2ecf20Sopenharmony_ci 58438c2ecf20Sopenharmony_cistatic int inet6_rtm_valid_getroute_req(struct sk_buff *skb, 58448c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh, 58458c2ecf20Sopenharmony_ci struct nlattr **tb, 58468c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 58478c2ecf20Sopenharmony_ci{ 58488c2ecf20Sopenharmony_ci struct rtmsg *rtm; 58498c2ecf20Sopenharmony_ci int i, err; 58508c2ecf20Sopenharmony_ci 58518c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 58528c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 58538c2ecf20Sopenharmony_ci "Invalid header for get route request"); 58548c2ecf20Sopenharmony_ci return -EINVAL; 58558c2ecf20Sopenharmony_ci } 58568c2ecf20Sopenharmony_ci 58578c2ecf20Sopenharmony_ci if (!netlink_strict_get_check(skb)) 58588c2ecf20Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 58598c2ecf20Sopenharmony_ci rtm_ipv6_policy, extack); 58608c2ecf20Sopenharmony_ci 58618c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 58628c2ecf20Sopenharmony_ci if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) || 58638c2ecf20Sopenharmony_ci (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) || 58648c2ecf20Sopenharmony_ci rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope || 58658c2ecf20Sopenharmony_ci rtm->rtm_type) { 58668c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request"); 58678c2ecf20Sopenharmony_ci return -EINVAL; 58688c2ecf20Sopenharmony_ci } 58698c2ecf20Sopenharmony_ci if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) { 58708c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 58718c2ecf20Sopenharmony_ci "Invalid flags for get route request"); 58728c2ecf20Sopenharmony_ci return -EINVAL; 58738c2ecf20Sopenharmony_ci } 58748c2ecf20Sopenharmony_ci 58758c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 58768c2ecf20Sopenharmony_ci rtm_ipv6_policy, extack); 58778c2ecf20Sopenharmony_ci if (err) 58788c2ecf20Sopenharmony_ci return err; 58798c2ecf20Sopenharmony_ci 58808c2ecf20Sopenharmony_ci if ((tb[RTA_SRC] && !rtm->rtm_src_len) || 58818c2ecf20Sopenharmony_ci (tb[RTA_DST] && !rtm->rtm_dst_len)) { 58828c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6"); 58838c2ecf20Sopenharmony_ci return -EINVAL; 58848c2ecf20Sopenharmony_ci } 58858c2ecf20Sopenharmony_ci 58868c2ecf20Sopenharmony_ci for (i = 0; i <= RTA_MAX; i++) { 58878c2ecf20Sopenharmony_ci if (!tb[i]) 58888c2ecf20Sopenharmony_ci continue; 58898c2ecf20Sopenharmony_ci 58908c2ecf20Sopenharmony_ci switch (i) { 58918c2ecf20Sopenharmony_ci case RTA_SRC: 58928c2ecf20Sopenharmony_ci case RTA_DST: 58938c2ecf20Sopenharmony_ci case RTA_IIF: 58948c2ecf20Sopenharmony_ci case RTA_OIF: 58958c2ecf20Sopenharmony_ci case RTA_MARK: 58968c2ecf20Sopenharmony_ci case RTA_UID: 58978c2ecf20Sopenharmony_ci case RTA_SPORT: 58988c2ecf20Sopenharmony_ci case RTA_DPORT: 58998c2ecf20Sopenharmony_ci case RTA_IP_PROTO: 59008c2ecf20Sopenharmony_ci break; 59018c2ecf20Sopenharmony_ci default: 59028c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request"); 59038c2ecf20Sopenharmony_ci return -EINVAL; 59048c2ecf20Sopenharmony_ci } 59058c2ecf20Sopenharmony_ci } 59068c2ecf20Sopenharmony_ci 59078c2ecf20Sopenharmony_ci return 0; 59088c2ecf20Sopenharmony_ci} 59098c2ecf20Sopenharmony_ci 59108c2ecf20Sopenharmony_cistatic int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 59118c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 59128c2ecf20Sopenharmony_ci{ 59138c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 59148c2ecf20Sopenharmony_ci struct nlattr *tb[RTA_MAX+1]; 59158c2ecf20Sopenharmony_ci int err, iif = 0, oif = 0; 59168c2ecf20Sopenharmony_ci struct fib6_info *from; 59178c2ecf20Sopenharmony_ci struct dst_entry *dst; 59188c2ecf20Sopenharmony_ci struct rt6_info *rt; 59198c2ecf20Sopenharmony_ci struct sk_buff *skb; 59208c2ecf20Sopenharmony_ci struct rtmsg *rtm; 59218c2ecf20Sopenharmony_ci struct flowi6 fl6 = {}; 59228c2ecf20Sopenharmony_ci bool fibmatch; 59238c2ecf20Sopenharmony_ci 59248c2ecf20Sopenharmony_ci err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack); 59258c2ecf20Sopenharmony_ci if (err < 0) 59268c2ecf20Sopenharmony_ci goto errout; 59278c2ecf20Sopenharmony_ci 59288c2ecf20Sopenharmony_ci err = -EINVAL; 59298c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 59308c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 59318c2ecf20Sopenharmony_ci fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH); 59328c2ecf20Sopenharmony_ci 59338c2ecf20Sopenharmony_ci if (tb[RTA_SRC]) { 59348c2ecf20Sopenharmony_ci if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 59358c2ecf20Sopenharmony_ci goto errout; 59368c2ecf20Sopenharmony_ci 59378c2ecf20Sopenharmony_ci fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 59388c2ecf20Sopenharmony_ci } 59398c2ecf20Sopenharmony_ci 59408c2ecf20Sopenharmony_ci if (tb[RTA_DST]) { 59418c2ecf20Sopenharmony_ci if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 59428c2ecf20Sopenharmony_ci goto errout; 59438c2ecf20Sopenharmony_ci 59448c2ecf20Sopenharmony_ci fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 59458c2ecf20Sopenharmony_ci } 59468c2ecf20Sopenharmony_ci 59478c2ecf20Sopenharmony_ci if (tb[RTA_IIF]) 59488c2ecf20Sopenharmony_ci iif = nla_get_u32(tb[RTA_IIF]); 59498c2ecf20Sopenharmony_ci 59508c2ecf20Sopenharmony_ci if (tb[RTA_OIF]) 59518c2ecf20Sopenharmony_ci oif = nla_get_u32(tb[RTA_OIF]); 59528c2ecf20Sopenharmony_ci 59538c2ecf20Sopenharmony_ci if (tb[RTA_MARK]) 59548c2ecf20Sopenharmony_ci fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 59558c2ecf20Sopenharmony_ci 59568c2ecf20Sopenharmony_ci if (tb[RTA_UID]) 59578c2ecf20Sopenharmony_ci fl6.flowi6_uid = make_kuid(current_user_ns(), 59588c2ecf20Sopenharmony_ci nla_get_u32(tb[RTA_UID])); 59598c2ecf20Sopenharmony_ci else 59608c2ecf20Sopenharmony_ci fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); 59618c2ecf20Sopenharmony_ci 59628c2ecf20Sopenharmony_ci if (tb[RTA_SPORT]) 59638c2ecf20Sopenharmony_ci fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]); 59648c2ecf20Sopenharmony_ci 59658c2ecf20Sopenharmony_ci if (tb[RTA_DPORT]) 59668c2ecf20Sopenharmony_ci fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]); 59678c2ecf20Sopenharmony_ci 59688c2ecf20Sopenharmony_ci if (tb[RTA_IP_PROTO]) { 59698c2ecf20Sopenharmony_ci err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO], 59708c2ecf20Sopenharmony_ci &fl6.flowi6_proto, AF_INET6, 59718c2ecf20Sopenharmony_ci extack); 59728c2ecf20Sopenharmony_ci if (err) 59738c2ecf20Sopenharmony_ci goto errout; 59748c2ecf20Sopenharmony_ci } 59758c2ecf20Sopenharmony_ci 59768c2ecf20Sopenharmony_ci if (iif) { 59778c2ecf20Sopenharmony_ci struct net_device *dev; 59788c2ecf20Sopenharmony_ci int flags = 0; 59798c2ecf20Sopenharmony_ci 59808c2ecf20Sopenharmony_ci rcu_read_lock(); 59818c2ecf20Sopenharmony_ci 59828c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(net, iif); 59838c2ecf20Sopenharmony_ci if (!dev) { 59848c2ecf20Sopenharmony_ci rcu_read_unlock(); 59858c2ecf20Sopenharmony_ci err = -ENODEV; 59868c2ecf20Sopenharmony_ci goto errout; 59878c2ecf20Sopenharmony_ci } 59888c2ecf20Sopenharmony_ci 59898c2ecf20Sopenharmony_ci fl6.flowi6_iif = iif; 59908c2ecf20Sopenharmony_ci 59918c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&fl6.saddr)) 59928c2ecf20Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 59938c2ecf20Sopenharmony_ci 59948c2ecf20Sopenharmony_ci dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags); 59958c2ecf20Sopenharmony_ci 59968c2ecf20Sopenharmony_ci rcu_read_unlock(); 59978c2ecf20Sopenharmony_ci } else { 59988c2ecf20Sopenharmony_ci fl6.flowi6_oif = oif; 59998c2ecf20Sopenharmony_ci 60008c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 60018c2ecf20Sopenharmony_ci } 60028c2ecf20Sopenharmony_ci 60038c2ecf20Sopenharmony_ci 60048c2ecf20Sopenharmony_ci rt = container_of(dst, struct rt6_info, dst); 60058c2ecf20Sopenharmony_ci if (rt->dst.error) { 60068c2ecf20Sopenharmony_ci err = rt->dst.error; 60078c2ecf20Sopenharmony_ci ip6_rt_put(rt); 60088c2ecf20Sopenharmony_ci goto errout; 60098c2ecf20Sopenharmony_ci } 60108c2ecf20Sopenharmony_ci 60118c2ecf20Sopenharmony_ci if (rt == net->ipv6.ip6_null_entry) { 60128c2ecf20Sopenharmony_ci err = rt->dst.error; 60138c2ecf20Sopenharmony_ci ip6_rt_put(rt); 60148c2ecf20Sopenharmony_ci goto errout; 60158c2ecf20Sopenharmony_ci } 60168c2ecf20Sopenharmony_ci 60178c2ecf20Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 60188c2ecf20Sopenharmony_ci if (!skb) { 60198c2ecf20Sopenharmony_ci ip6_rt_put(rt); 60208c2ecf20Sopenharmony_ci err = -ENOBUFS; 60218c2ecf20Sopenharmony_ci goto errout; 60228c2ecf20Sopenharmony_ci } 60238c2ecf20Sopenharmony_ci 60248c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 60258c2ecf20Sopenharmony_ci 60268c2ecf20Sopenharmony_ci rcu_read_lock(); 60278c2ecf20Sopenharmony_ci from = rcu_dereference(rt->from); 60288c2ecf20Sopenharmony_ci if (from) { 60298c2ecf20Sopenharmony_ci if (fibmatch) 60308c2ecf20Sopenharmony_ci err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, 60318c2ecf20Sopenharmony_ci iif, RTM_NEWROUTE, 60328c2ecf20Sopenharmony_ci NETLINK_CB(in_skb).portid, 60338c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 0); 60348c2ecf20Sopenharmony_ci else 60358c2ecf20Sopenharmony_ci err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, 60368c2ecf20Sopenharmony_ci &fl6.saddr, iif, RTM_NEWROUTE, 60378c2ecf20Sopenharmony_ci NETLINK_CB(in_skb).portid, 60388c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 0); 60398c2ecf20Sopenharmony_ci } else { 60408c2ecf20Sopenharmony_ci err = -ENETUNREACH; 60418c2ecf20Sopenharmony_ci } 60428c2ecf20Sopenharmony_ci rcu_read_unlock(); 60438c2ecf20Sopenharmony_ci 60448c2ecf20Sopenharmony_ci if (err < 0) { 60458c2ecf20Sopenharmony_ci kfree_skb(skb); 60468c2ecf20Sopenharmony_ci goto errout; 60478c2ecf20Sopenharmony_ci } 60488c2ecf20Sopenharmony_ci 60498c2ecf20Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 60508c2ecf20Sopenharmony_cierrout: 60518c2ecf20Sopenharmony_ci return err; 60528c2ecf20Sopenharmony_ci} 60538c2ecf20Sopenharmony_ci 60548c2ecf20Sopenharmony_civoid inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info, 60558c2ecf20Sopenharmony_ci unsigned int nlm_flags) 60568c2ecf20Sopenharmony_ci{ 60578c2ecf20Sopenharmony_ci struct sk_buff *skb; 60588c2ecf20Sopenharmony_ci struct net *net = info->nl_net; 60598c2ecf20Sopenharmony_ci u32 seq; 60608c2ecf20Sopenharmony_ci int err; 60618c2ecf20Sopenharmony_ci 60628c2ecf20Sopenharmony_ci err = -ENOBUFS; 60638c2ecf20Sopenharmony_ci seq = info->nlh ? info->nlh->nlmsg_seq : 0; 60648c2ecf20Sopenharmony_ci 60658c2ecf20Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 60668c2ecf20Sopenharmony_ci if (!skb) 60678c2ecf20Sopenharmony_ci goto errout; 60688c2ecf20Sopenharmony_ci 60698c2ecf20Sopenharmony_ci err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0, 60708c2ecf20Sopenharmony_ci event, info->portid, seq, nlm_flags); 60718c2ecf20Sopenharmony_ci if (err < 0) { 60728c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 60738c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 60748c2ecf20Sopenharmony_ci kfree_skb(skb); 60758c2ecf20Sopenharmony_ci goto errout; 60768c2ecf20Sopenharmony_ci } 60778c2ecf20Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 60788c2ecf20Sopenharmony_ci info->nlh, gfp_any()); 60798c2ecf20Sopenharmony_ci return; 60808c2ecf20Sopenharmony_cierrout: 60818c2ecf20Sopenharmony_ci if (err < 0) 60828c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 60838c2ecf20Sopenharmony_ci} 60848c2ecf20Sopenharmony_ci 60858c2ecf20Sopenharmony_civoid fib6_rt_update(struct net *net, struct fib6_info *rt, 60868c2ecf20Sopenharmony_ci struct nl_info *info) 60878c2ecf20Sopenharmony_ci{ 60888c2ecf20Sopenharmony_ci u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 60898c2ecf20Sopenharmony_ci struct sk_buff *skb; 60908c2ecf20Sopenharmony_ci int err = -ENOBUFS; 60918c2ecf20Sopenharmony_ci 60928c2ecf20Sopenharmony_ci /* call_fib6_entry_notifiers will be removed when in-kernel notifier 60938c2ecf20Sopenharmony_ci * is implemented and supported for nexthop objects 60948c2ecf20Sopenharmony_ci */ 60958c2ecf20Sopenharmony_ci call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, rt, NULL); 60968c2ecf20Sopenharmony_ci 60978c2ecf20Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 60988c2ecf20Sopenharmony_ci if (!skb) 60998c2ecf20Sopenharmony_ci goto errout; 61008c2ecf20Sopenharmony_ci 61018c2ecf20Sopenharmony_ci err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0, 61028c2ecf20Sopenharmony_ci RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE); 61038c2ecf20Sopenharmony_ci if (err < 0) { 61048c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 61058c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 61068c2ecf20Sopenharmony_ci kfree_skb(skb); 61078c2ecf20Sopenharmony_ci goto errout; 61088c2ecf20Sopenharmony_ci } 61098c2ecf20Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 61108c2ecf20Sopenharmony_ci info->nlh, gfp_any()); 61118c2ecf20Sopenharmony_ci return; 61128c2ecf20Sopenharmony_cierrout: 61138c2ecf20Sopenharmony_ci if (err < 0) 61148c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 61158c2ecf20Sopenharmony_ci} 61168c2ecf20Sopenharmony_ci 61178c2ecf20Sopenharmony_cistatic int ip6_route_dev_notify(struct notifier_block *this, 61188c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 61198c2ecf20Sopenharmony_ci{ 61208c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 61218c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 61228c2ecf20Sopenharmony_ci 61238c2ecf20Sopenharmony_ci if (!(dev->flags & IFF_LOOPBACK)) 61248c2ecf20Sopenharmony_ci return NOTIFY_OK; 61258c2ecf20Sopenharmony_ci 61268c2ecf20Sopenharmony_ci if (event == NETDEV_REGISTER) { 61278c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev; 61288c2ecf20Sopenharmony_ci net->ipv6.ip6_null_entry->dst.dev = dev; 61298c2ecf20Sopenharmony_ci net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 61308c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 61318c2ecf20Sopenharmony_ci net->ipv6.ip6_prohibit_entry->dst.dev = dev; 61328c2ecf20Sopenharmony_ci net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 61338c2ecf20Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 61348c2ecf20Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 61358c2ecf20Sopenharmony_ci#endif 61368c2ecf20Sopenharmony_ci } else if (event == NETDEV_UNREGISTER && 61378c2ecf20Sopenharmony_ci dev->reg_state != NETREG_UNREGISTERED) { 61388c2ecf20Sopenharmony_ci /* NETDEV_UNREGISTER could be fired for multiple times by 61398c2ecf20Sopenharmony_ci * netdev_wait_allrefs(). Make sure we only call this once. 61408c2ecf20Sopenharmony_ci */ 61418c2ecf20Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); 61428c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 61438c2ecf20Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); 61448c2ecf20Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); 61458c2ecf20Sopenharmony_ci#endif 61468c2ecf20Sopenharmony_ci } 61478c2ecf20Sopenharmony_ci 61488c2ecf20Sopenharmony_ci return NOTIFY_OK; 61498c2ecf20Sopenharmony_ci} 61508c2ecf20Sopenharmony_ci 61518c2ecf20Sopenharmony_ci/* 61528c2ecf20Sopenharmony_ci * /proc 61538c2ecf20Sopenharmony_ci */ 61548c2ecf20Sopenharmony_ci 61558c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 61568c2ecf20Sopenharmony_cistatic int rt6_stats_seq_show(struct seq_file *seq, void *v) 61578c2ecf20Sopenharmony_ci{ 61588c2ecf20Sopenharmony_ci struct net *net = (struct net *)seq->private; 61598c2ecf20Sopenharmony_ci seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 61608c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_nodes, 61618c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_route_nodes, 61628c2ecf20Sopenharmony_ci atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc), 61638c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_entries, 61648c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache, 61658c2ecf20Sopenharmony_ci dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 61668c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_discarded_routes); 61678c2ecf20Sopenharmony_ci 61688c2ecf20Sopenharmony_ci return 0; 61698c2ecf20Sopenharmony_ci} 61708c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 61718c2ecf20Sopenharmony_ci 61728c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 61738c2ecf20Sopenharmony_ci 61748c2ecf20Sopenharmony_cistatic int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 61758c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 61768c2ecf20Sopenharmony_ci{ 61778c2ecf20Sopenharmony_ci struct net *net; 61788c2ecf20Sopenharmony_ci int delay; 61798c2ecf20Sopenharmony_ci int ret; 61808c2ecf20Sopenharmony_ci if (!write) 61818c2ecf20Sopenharmony_ci return -EINVAL; 61828c2ecf20Sopenharmony_ci 61838c2ecf20Sopenharmony_ci net = (struct net *)ctl->extra1; 61848c2ecf20Sopenharmony_ci delay = net->ipv6.sysctl.flush_delay; 61858c2ecf20Sopenharmony_ci ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 61868c2ecf20Sopenharmony_ci if (ret) 61878c2ecf20Sopenharmony_ci return ret; 61888c2ecf20Sopenharmony_ci 61898c2ecf20Sopenharmony_ci fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 61908c2ecf20Sopenharmony_ci return 0; 61918c2ecf20Sopenharmony_ci} 61928c2ecf20Sopenharmony_ci 61938c2ecf20Sopenharmony_cistatic struct ctl_table ipv6_route_table_template[] = { 61948c2ecf20Sopenharmony_ci { 61958c2ecf20Sopenharmony_ci .procname = "flush", 61968c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.flush_delay, 61978c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 61988c2ecf20Sopenharmony_ci .mode = 0200, 61998c2ecf20Sopenharmony_ci .proc_handler = ipv6_sysctl_rtcache_flush 62008c2ecf20Sopenharmony_ci }, 62018c2ecf20Sopenharmony_ci { 62028c2ecf20Sopenharmony_ci .procname = "gc_thresh", 62038c2ecf20Sopenharmony_ci .data = &ip6_dst_ops_template.gc_thresh, 62048c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62058c2ecf20Sopenharmony_ci .mode = 0644, 62068c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 62078c2ecf20Sopenharmony_ci }, 62088c2ecf20Sopenharmony_ci { 62098c2ecf20Sopenharmony_ci .procname = "max_size", 62108c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 62118c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62128c2ecf20Sopenharmony_ci .mode = 0644, 62138c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 62148c2ecf20Sopenharmony_ci }, 62158c2ecf20Sopenharmony_ci { 62168c2ecf20Sopenharmony_ci .procname = "gc_min_interval", 62178c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 62188c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62198c2ecf20Sopenharmony_ci .mode = 0644, 62208c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 62218c2ecf20Sopenharmony_ci }, 62228c2ecf20Sopenharmony_ci { 62238c2ecf20Sopenharmony_ci .procname = "gc_timeout", 62248c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 62258c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62268c2ecf20Sopenharmony_ci .mode = 0644, 62278c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 62288c2ecf20Sopenharmony_ci }, 62298c2ecf20Sopenharmony_ci { 62308c2ecf20Sopenharmony_ci .procname = "gc_interval", 62318c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 62328c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62338c2ecf20Sopenharmony_ci .mode = 0644, 62348c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 62358c2ecf20Sopenharmony_ci }, 62368c2ecf20Sopenharmony_ci { 62378c2ecf20Sopenharmony_ci .procname = "gc_elasticity", 62388c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 62398c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62408c2ecf20Sopenharmony_ci .mode = 0644, 62418c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 62428c2ecf20Sopenharmony_ci }, 62438c2ecf20Sopenharmony_ci { 62448c2ecf20Sopenharmony_ci .procname = "mtu_expires", 62458c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 62468c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62478c2ecf20Sopenharmony_ci .mode = 0644, 62488c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 62498c2ecf20Sopenharmony_ci }, 62508c2ecf20Sopenharmony_ci { 62518c2ecf20Sopenharmony_ci .procname = "min_adv_mss", 62528c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 62538c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62548c2ecf20Sopenharmony_ci .mode = 0644, 62558c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 62568c2ecf20Sopenharmony_ci }, 62578c2ecf20Sopenharmony_ci { 62588c2ecf20Sopenharmony_ci .procname = "gc_min_interval_ms", 62598c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 62608c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62618c2ecf20Sopenharmony_ci .mode = 0644, 62628c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 62638c2ecf20Sopenharmony_ci }, 62648c2ecf20Sopenharmony_ci { 62658c2ecf20Sopenharmony_ci .procname = "skip_notify_on_dev_down", 62668c2ecf20Sopenharmony_ci .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down, 62678c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 62688c2ecf20Sopenharmony_ci .mode = 0644, 62698c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 62708c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ZERO, 62718c2ecf20Sopenharmony_ci .extra2 = SYSCTL_ONE, 62728c2ecf20Sopenharmony_ci }, 62738c2ecf20Sopenharmony_ci { } 62748c2ecf20Sopenharmony_ci}; 62758c2ecf20Sopenharmony_ci 62768c2ecf20Sopenharmony_cistruct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 62778c2ecf20Sopenharmony_ci{ 62788c2ecf20Sopenharmony_ci struct ctl_table *table; 62798c2ecf20Sopenharmony_ci 62808c2ecf20Sopenharmony_ci table = kmemdup(ipv6_route_table_template, 62818c2ecf20Sopenharmony_ci sizeof(ipv6_route_table_template), 62828c2ecf20Sopenharmony_ci GFP_KERNEL); 62838c2ecf20Sopenharmony_ci 62848c2ecf20Sopenharmony_ci if (table) { 62858c2ecf20Sopenharmony_ci table[0].data = &net->ipv6.sysctl.flush_delay; 62868c2ecf20Sopenharmony_ci table[0].extra1 = net; 62878c2ecf20Sopenharmony_ci table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 62888c2ecf20Sopenharmony_ci table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 62898c2ecf20Sopenharmony_ci table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 62908c2ecf20Sopenharmony_ci table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 62918c2ecf20Sopenharmony_ci table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 62928c2ecf20Sopenharmony_ci table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 62938c2ecf20Sopenharmony_ci table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 62948c2ecf20Sopenharmony_ci table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 62958c2ecf20Sopenharmony_ci table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 62968c2ecf20Sopenharmony_ci table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down; 62978c2ecf20Sopenharmony_ci 62988c2ecf20Sopenharmony_ci /* Don't export sysctls to unprivileged users */ 62998c2ecf20Sopenharmony_ci if (net->user_ns != &init_user_ns) 63008c2ecf20Sopenharmony_ci table[0].procname = NULL; 63018c2ecf20Sopenharmony_ci } 63028c2ecf20Sopenharmony_ci 63038c2ecf20Sopenharmony_ci return table; 63048c2ecf20Sopenharmony_ci} 63058c2ecf20Sopenharmony_ci#endif 63068c2ecf20Sopenharmony_ci 63078c2ecf20Sopenharmony_cistatic int __net_init ip6_route_net_init(struct net *net) 63088c2ecf20Sopenharmony_ci{ 63098c2ecf20Sopenharmony_ci int ret = -ENOMEM; 63108c2ecf20Sopenharmony_ci 63118c2ecf20Sopenharmony_ci memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 63128c2ecf20Sopenharmony_ci sizeof(net->ipv6.ip6_dst_ops)); 63138c2ecf20Sopenharmony_ci 63148c2ecf20Sopenharmony_ci if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 63158c2ecf20Sopenharmony_ci goto out_ip6_dst_ops; 63168c2ecf20Sopenharmony_ci 63178c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true); 63188c2ecf20Sopenharmony_ci if (!net->ipv6.fib6_null_entry) 63198c2ecf20Sopenharmony_ci goto out_ip6_dst_entries; 63208c2ecf20Sopenharmony_ci memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template, 63218c2ecf20Sopenharmony_ci sizeof(*net->ipv6.fib6_null_entry)); 63228c2ecf20Sopenharmony_ci 63238c2ecf20Sopenharmony_ci net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 63248c2ecf20Sopenharmony_ci sizeof(*net->ipv6.ip6_null_entry), 63258c2ecf20Sopenharmony_ci GFP_KERNEL); 63268c2ecf20Sopenharmony_ci if (!net->ipv6.ip6_null_entry) 63278c2ecf20Sopenharmony_ci goto out_fib6_null_entry; 63288c2ecf20Sopenharmony_ci net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 63298c2ecf20Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 63308c2ecf20Sopenharmony_ci ip6_template_metrics, true); 63318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->rt6i_uncached); 63328c2ecf20Sopenharmony_ci 63338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 63348c2ecf20Sopenharmony_ci net->ipv6.fib6_has_custom_rules = false; 63358c2ecf20Sopenharmony_ci net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 63368c2ecf20Sopenharmony_ci sizeof(*net->ipv6.ip6_prohibit_entry), 63378c2ecf20Sopenharmony_ci GFP_KERNEL); 63388c2ecf20Sopenharmony_ci if (!net->ipv6.ip6_prohibit_entry) 63398c2ecf20Sopenharmony_ci goto out_ip6_null_entry; 63408c2ecf20Sopenharmony_ci net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 63418c2ecf20Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 63428c2ecf20Sopenharmony_ci ip6_template_metrics, true); 63438c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); 63448c2ecf20Sopenharmony_ci 63458c2ecf20Sopenharmony_ci net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 63468c2ecf20Sopenharmony_ci sizeof(*net->ipv6.ip6_blk_hole_entry), 63478c2ecf20Sopenharmony_ci GFP_KERNEL); 63488c2ecf20Sopenharmony_ci if (!net->ipv6.ip6_blk_hole_entry) 63498c2ecf20Sopenharmony_ci goto out_ip6_prohibit_entry; 63508c2ecf20Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 63518c2ecf20Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 63528c2ecf20Sopenharmony_ci ip6_template_metrics, true); 63538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->rt6i_uncached); 63548c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 63558c2ecf20Sopenharmony_ci net->ipv6.fib6_routes_require_src = 0; 63568c2ecf20Sopenharmony_ci#endif 63578c2ecf20Sopenharmony_ci#endif 63588c2ecf20Sopenharmony_ci 63598c2ecf20Sopenharmony_ci net->ipv6.sysctl.flush_delay = 0; 63608c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_max_size = INT_MAX; 63618c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 63628c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 63638c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 63648c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 63658c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 63668c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 63678c2ecf20Sopenharmony_ci net->ipv6.sysctl.skip_notify_on_dev_down = 0; 63688c2ecf20Sopenharmony_ci 63698c2ecf20Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, 30*HZ); 63708c2ecf20Sopenharmony_ci 63718c2ecf20Sopenharmony_ci ret = 0; 63728c2ecf20Sopenharmony_ciout: 63738c2ecf20Sopenharmony_ci return ret; 63748c2ecf20Sopenharmony_ci 63758c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 63768c2ecf20Sopenharmony_ciout_ip6_prohibit_entry: 63778c2ecf20Sopenharmony_ci kfree(net->ipv6.ip6_prohibit_entry); 63788c2ecf20Sopenharmony_ciout_ip6_null_entry: 63798c2ecf20Sopenharmony_ci kfree(net->ipv6.ip6_null_entry); 63808c2ecf20Sopenharmony_ci#endif 63818c2ecf20Sopenharmony_ciout_fib6_null_entry: 63828c2ecf20Sopenharmony_ci kfree(net->ipv6.fib6_null_entry); 63838c2ecf20Sopenharmony_ciout_ip6_dst_entries: 63848c2ecf20Sopenharmony_ci dst_entries_destroy(&net->ipv6.ip6_dst_ops); 63858c2ecf20Sopenharmony_ciout_ip6_dst_ops: 63868c2ecf20Sopenharmony_ci goto out; 63878c2ecf20Sopenharmony_ci} 63888c2ecf20Sopenharmony_ci 63898c2ecf20Sopenharmony_cistatic void __net_exit ip6_route_net_exit(struct net *net) 63908c2ecf20Sopenharmony_ci{ 63918c2ecf20Sopenharmony_ci kfree(net->ipv6.fib6_null_entry); 63928c2ecf20Sopenharmony_ci kfree(net->ipv6.ip6_null_entry); 63938c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 63948c2ecf20Sopenharmony_ci kfree(net->ipv6.ip6_prohibit_entry); 63958c2ecf20Sopenharmony_ci kfree(net->ipv6.ip6_blk_hole_entry); 63968c2ecf20Sopenharmony_ci#endif 63978c2ecf20Sopenharmony_ci dst_entries_destroy(&net->ipv6.ip6_dst_ops); 63988c2ecf20Sopenharmony_ci} 63998c2ecf20Sopenharmony_ci 64008c2ecf20Sopenharmony_cistatic int __net_init ip6_route_net_init_late(struct net *net) 64018c2ecf20Sopenharmony_ci{ 64028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 64038c2ecf20Sopenharmony_ci if (!proc_create_net("ipv6_route", 0, net->proc_net, 64048c2ecf20Sopenharmony_ci &ipv6_route_seq_ops, 64058c2ecf20Sopenharmony_ci sizeof(struct ipv6_route_iter))) 64068c2ecf20Sopenharmony_ci return -ENOMEM; 64078c2ecf20Sopenharmony_ci 64088c2ecf20Sopenharmony_ci if (!proc_create_net_single("rt6_stats", 0444, net->proc_net, 64098c2ecf20Sopenharmony_ci rt6_stats_seq_show, NULL)) { 64108c2ecf20Sopenharmony_ci remove_proc_entry("ipv6_route", net->proc_net); 64118c2ecf20Sopenharmony_ci return -ENOMEM; 64128c2ecf20Sopenharmony_ci } 64138c2ecf20Sopenharmony_ci#endif 64148c2ecf20Sopenharmony_ci return 0; 64158c2ecf20Sopenharmony_ci} 64168c2ecf20Sopenharmony_ci 64178c2ecf20Sopenharmony_cistatic void __net_exit ip6_route_net_exit_late(struct net *net) 64188c2ecf20Sopenharmony_ci{ 64198c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 64208c2ecf20Sopenharmony_ci remove_proc_entry("ipv6_route", net->proc_net); 64218c2ecf20Sopenharmony_ci remove_proc_entry("rt6_stats", net->proc_net); 64228c2ecf20Sopenharmony_ci#endif 64238c2ecf20Sopenharmony_ci} 64248c2ecf20Sopenharmony_ci 64258c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_route_net_ops = { 64268c2ecf20Sopenharmony_ci .init = ip6_route_net_init, 64278c2ecf20Sopenharmony_ci .exit = ip6_route_net_exit, 64288c2ecf20Sopenharmony_ci}; 64298c2ecf20Sopenharmony_ci 64308c2ecf20Sopenharmony_cistatic int __net_init ipv6_inetpeer_init(struct net *net) 64318c2ecf20Sopenharmony_ci{ 64328c2ecf20Sopenharmony_ci struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 64338c2ecf20Sopenharmony_ci 64348c2ecf20Sopenharmony_ci if (!bp) 64358c2ecf20Sopenharmony_ci return -ENOMEM; 64368c2ecf20Sopenharmony_ci inet_peer_base_init(bp); 64378c2ecf20Sopenharmony_ci net->ipv6.peers = bp; 64388c2ecf20Sopenharmony_ci return 0; 64398c2ecf20Sopenharmony_ci} 64408c2ecf20Sopenharmony_ci 64418c2ecf20Sopenharmony_cistatic void __net_exit ipv6_inetpeer_exit(struct net *net) 64428c2ecf20Sopenharmony_ci{ 64438c2ecf20Sopenharmony_ci struct inet_peer_base *bp = net->ipv6.peers; 64448c2ecf20Sopenharmony_ci 64458c2ecf20Sopenharmony_ci net->ipv6.peers = NULL; 64468c2ecf20Sopenharmony_ci inetpeer_invalidate_tree(bp); 64478c2ecf20Sopenharmony_ci kfree(bp); 64488c2ecf20Sopenharmony_ci} 64498c2ecf20Sopenharmony_ci 64508c2ecf20Sopenharmony_cistatic struct pernet_operations ipv6_inetpeer_ops = { 64518c2ecf20Sopenharmony_ci .init = ipv6_inetpeer_init, 64528c2ecf20Sopenharmony_ci .exit = ipv6_inetpeer_exit, 64538c2ecf20Sopenharmony_ci}; 64548c2ecf20Sopenharmony_ci 64558c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_route_net_late_ops = { 64568c2ecf20Sopenharmony_ci .init = ip6_route_net_init_late, 64578c2ecf20Sopenharmony_ci .exit = ip6_route_net_exit_late, 64588c2ecf20Sopenharmony_ci}; 64598c2ecf20Sopenharmony_ci 64608c2ecf20Sopenharmony_cistatic struct notifier_block ip6_route_dev_notifier = { 64618c2ecf20Sopenharmony_ci .notifier_call = ip6_route_dev_notify, 64628c2ecf20Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY - 10, 64638c2ecf20Sopenharmony_ci}; 64648c2ecf20Sopenharmony_ci 64658c2ecf20Sopenharmony_civoid __init ip6_route_init_special_entries(void) 64668c2ecf20Sopenharmony_ci{ 64678c2ecf20Sopenharmony_ci /* Registering of the loopback is done before this portion of code, 64688c2ecf20Sopenharmony_ci * the loopback reference in rt6_info will not be taken, do it 64698c2ecf20Sopenharmony_ci * manually for init_net */ 64708c2ecf20Sopenharmony_ci init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev; 64718c2ecf20Sopenharmony_ci init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 64728c2ecf20Sopenharmony_ci init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 64738c2ecf20Sopenharmony_ci #ifdef CONFIG_IPV6_MULTIPLE_TABLES 64748c2ecf20Sopenharmony_ci init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 64758c2ecf20Sopenharmony_ci init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 64768c2ecf20Sopenharmony_ci init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 64778c2ecf20Sopenharmony_ci init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 64788c2ecf20Sopenharmony_ci #endif 64798c2ecf20Sopenharmony_ci} 64808c2ecf20Sopenharmony_ci 64818c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 64828c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 64838c2ecf20Sopenharmony_ciDEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt) 64848c2ecf20Sopenharmony_ci 64858c2ecf20Sopenharmony_ciBTF_ID_LIST(btf_fib6_info_id) 64868c2ecf20Sopenharmony_ciBTF_ID(struct, fib6_info) 64878c2ecf20Sopenharmony_ci 64888c2ecf20Sopenharmony_cistatic const struct bpf_iter_seq_info ipv6_route_seq_info = { 64898c2ecf20Sopenharmony_ci .seq_ops = &ipv6_route_seq_ops, 64908c2ecf20Sopenharmony_ci .init_seq_private = bpf_iter_init_seq_net, 64918c2ecf20Sopenharmony_ci .fini_seq_private = bpf_iter_fini_seq_net, 64928c2ecf20Sopenharmony_ci .seq_priv_size = sizeof(struct ipv6_route_iter), 64938c2ecf20Sopenharmony_ci}; 64948c2ecf20Sopenharmony_ci 64958c2ecf20Sopenharmony_cistatic struct bpf_iter_reg ipv6_route_reg_info = { 64968c2ecf20Sopenharmony_ci .target = "ipv6_route", 64978c2ecf20Sopenharmony_ci .ctx_arg_info_size = 1, 64988c2ecf20Sopenharmony_ci .ctx_arg_info = { 64998c2ecf20Sopenharmony_ci { offsetof(struct bpf_iter__ipv6_route, rt), 65008c2ecf20Sopenharmony_ci PTR_TO_BTF_ID_OR_NULL }, 65018c2ecf20Sopenharmony_ci }, 65028c2ecf20Sopenharmony_ci .seq_info = &ipv6_route_seq_info, 65038c2ecf20Sopenharmony_ci}; 65048c2ecf20Sopenharmony_ci 65058c2ecf20Sopenharmony_cistatic int __init bpf_iter_register(void) 65068c2ecf20Sopenharmony_ci{ 65078c2ecf20Sopenharmony_ci ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id; 65088c2ecf20Sopenharmony_ci return bpf_iter_reg_target(&ipv6_route_reg_info); 65098c2ecf20Sopenharmony_ci} 65108c2ecf20Sopenharmony_ci 65118c2ecf20Sopenharmony_cistatic void bpf_iter_unregister(void) 65128c2ecf20Sopenharmony_ci{ 65138c2ecf20Sopenharmony_ci bpf_iter_unreg_target(&ipv6_route_reg_info); 65148c2ecf20Sopenharmony_ci} 65158c2ecf20Sopenharmony_ci#endif 65168c2ecf20Sopenharmony_ci#endif 65178c2ecf20Sopenharmony_ci 65188c2ecf20Sopenharmony_ciint __init ip6_route_init(void) 65198c2ecf20Sopenharmony_ci{ 65208c2ecf20Sopenharmony_ci int ret; 65218c2ecf20Sopenharmony_ci int cpu; 65228c2ecf20Sopenharmony_ci 65238c2ecf20Sopenharmony_ci ret = -ENOMEM; 65248c2ecf20Sopenharmony_ci ip6_dst_ops_template.kmem_cachep = 65258c2ecf20Sopenharmony_ci kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 65268c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 65278c2ecf20Sopenharmony_ci if (!ip6_dst_ops_template.kmem_cachep) 65288c2ecf20Sopenharmony_ci goto out; 65298c2ecf20Sopenharmony_ci 65308c2ecf20Sopenharmony_ci ret = dst_entries_init(&ip6_dst_blackhole_ops); 65318c2ecf20Sopenharmony_ci if (ret) 65328c2ecf20Sopenharmony_ci goto out_kmem_cache; 65338c2ecf20Sopenharmony_ci 65348c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&ipv6_inetpeer_ops); 65358c2ecf20Sopenharmony_ci if (ret) 65368c2ecf20Sopenharmony_ci goto out_dst_entries; 65378c2ecf20Sopenharmony_ci 65388c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&ip6_route_net_ops); 65398c2ecf20Sopenharmony_ci if (ret) 65408c2ecf20Sopenharmony_ci goto out_register_inetpeer; 65418c2ecf20Sopenharmony_ci 65428c2ecf20Sopenharmony_ci ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 65438c2ecf20Sopenharmony_ci 65448c2ecf20Sopenharmony_ci ret = fib6_init(); 65458c2ecf20Sopenharmony_ci if (ret) 65468c2ecf20Sopenharmony_ci goto out_register_subsys; 65478c2ecf20Sopenharmony_ci 65488c2ecf20Sopenharmony_ci ret = xfrm6_init(); 65498c2ecf20Sopenharmony_ci if (ret) 65508c2ecf20Sopenharmony_ci goto out_fib6_init; 65518c2ecf20Sopenharmony_ci 65528c2ecf20Sopenharmony_ci ret = fib6_rules_init(); 65538c2ecf20Sopenharmony_ci if (ret) 65548c2ecf20Sopenharmony_ci goto xfrm6_init; 65558c2ecf20Sopenharmony_ci 65568c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&ip6_route_net_late_ops); 65578c2ecf20Sopenharmony_ci if (ret) 65588c2ecf20Sopenharmony_ci goto fib6_rules_init; 65598c2ecf20Sopenharmony_ci 65608c2ecf20Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE, 65618c2ecf20Sopenharmony_ci inet6_rtm_newroute, NULL, 0); 65628c2ecf20Sopenharmony_ci if (ret < 0) 65638c2ecf20Sopenharmony_ci goto out_register_late_subsys; 65648c2ecf20Sopenharmony_ci 65658c2ecf20Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE, 65668c2ecf20Sopenharmony_ci inet6_rtm_delroute, NULL, 0); 65678c2ecf20Sopenharmony_ci if (ret < 0) 65688c2ecf20Sopenharmony_ci goto out_register_late_subsys; 65698c2ecf20Sopenharmony_ci 65708c2ecf20Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, 65718c2ecf20Sopenharmony_ci inet6_rtm_getroute, NULL, 65728c2ecf20Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 65738c2ecf20Sopenharmony_ci if (ret < 0) 65748c2ecf20Sopenharmony_ci goto out_register_late_subsys; 65758c2ecf20Sopenharmony_ci 65768c2ecf20Sopenharmony_ci ret = register_netdevice_notifier(&ip6_route_dev_notifier); 65778c2ecf20Sopenharmony_ci if (ret) 65788c2ecf20Sopenharmony_ci goto out_register_late_subsys; 65798c2ecf20Sopenharmony_ci 65808c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 65818c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 65828c2ecf20Sopenharmony_ci ret = bpf_iter_register(); 65838c2ecf20Sopenharmony_ci if (ret) 65848c2ecf20Sopenharmony_ci goto out_register_late_subsys; 65858c2ecf20Sopenharmony_ci#endif 65868c2ecf20Sopenharmony_ci#endif 65878c2ecf20Sopenharmony_ci 65888c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 65898c2ecf20Sopenharmony_ci struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 65908c2ecf20Sopenharmony_ci 65918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ul->head); 65928c2ecf20Sopenharmony_ci spin_lock_init(&ul->lock); 65938c2ecf20Sopenharmony_ci } 65948c2ecf20Sopenharmony_ci 65958c2ecf20Sopenharmony_ciout: 65968c2ecf20Sopenharmony_ci return ret; 65978c2ecf20Sopenharmony_ci 65988c2ecf20Sopenharmony_ciout_register_late_subsys: 65998c2ecf20Sopenharmony_ci rtnl_unregister_all(PF_INET6); 66008c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_late_ops); 66018c2ecf20Sopenharmony_cifib6_rules_init: 66028c2ecf20Sopenharmony_ci fib6_rules_cleanup(); 66038c2ecf20Sopenharmony_cixfrm6_init: 66048c2ecf20Sopenharmony_ci xfrm6_fini(); 66058c2ecf20Sopenharmony_ciout_fib6_init: 66068c2ecf20Sopenharmony_ci fib6_gc_cleanup(); 66078c2ecf20Sopenharmony_ciout_register_subsys: 66088c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_ops); 66098c2ecf20Sopenharmony_ciout_register_inetpeer: 66108c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ipv6_inetpeer_ops); 66118c2ecf20Sopenharmony_ciout_dst_entries: 66128c2ecf20Sopenharmony_ci dst_entries_destroy(&ip6_dst_blackhole_ops); 66138c2ecf20Sopenharmony_ciout_kmem_cache: 66148c2ecf20Sopenharmony_ci kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 66158c2ecf20Sopenharmony_ci goto out; 66168c2ecf20Sopenharmony_ci} 66178c2ecf20Sopenharmony_ci 66188c2ecf20Sopenharmony_civoid ip6_route_cleanup(void) 66198c2ecf20Sopenharmony_ci{ 66208c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 66218c2ecf20Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 66228c2ecf20Sopenharmony_ci bpf_iter_unregister(); 66238c2ecf20Sopenharmony_ci#endif 66248c2ecf20Sopenharmony_ci#endif 66258c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ip6_route_dev_notifier); 66268c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_late_ops); 66278c2ecf20Sopenharmony_ci fib6_rules_cleanup(); 66288c2ecf20Sopenharmony_ci xfrm6_fini(); 66298c2ecf20Sopenharmony_ci fib6_gc_cleanup(); 66308c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ipv6_inetpeer_ops); 66318c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_ops); 66328c2ecf20Sopenharmony_ci dst_entries_destroy(&ip6_dst_blackhole_ops); 66338c2ecf20Sopenharmony_ci kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 66348c2ecf20Sopenharmony_ci} 6635