162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux INET6 implementation 462306a36Sopenharmony_ci * FIB front-end. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* Changes: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI 1362306a36Sopenharmony_ci * reworked default router selection. 1462306a36Sopenharmony_ci * - respect outgoing interface 1562306a36Sopenharmony_ci * - select from (probably) reachable routers (i.e. 1662306a36Sopenharmony_ci * routers in REACHABLE, STALE, DELAY or PROBE states). 1762306a36Sopenharmony_ci * - always select the same router if it is (probably) 1862306a36Sopenharmony_ci * reachable. otherwise, round-robin the list. 1962306a36Sopenharmony_ci * Ville Nuorvala 2062306a36Sopenharmony_ci * Fixed routing subtrees. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/capability.h> 2662306a36Sopenharmony_ci#include <linux/errno.h> 2762306a36Sopenharmony_ci#include <linux/export.h> 2862306a36Sopenharmony_ci#include <linux/types.h> 2962306a36Sopenharmony_ci#include <linux/times.h> 3062306a36Sopenharmony_ci#include <linux/socket.h> 3162306a36Sopenharmony_ci#include <linux/sockios.h> 3262306a36Sopenharmony_ci#include <linux/net.h> 3362306a36Sopenharmony_ci#include <linux/route.h> 3462306a36Sopenharmony_ci#include <linux/netdevice.h> 3562306a36Sopenharmony_ci#include <linux/in6.h> 3662306a36Sopenharmony_ci#include <linux/mroute6.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/if_arp.h> 3962306a36Sopenharmony_ci#include <linux/proc_fs.h> 4062306a36Sopenharmony_ci#include <linux/seq_file.h> 4162306a36Sopenharmony_ci#include <linux/nsproxy.h> 4262306a36Sopenharmony_ci#include <linux/slab.h> 4362306a36Sopenharmony_ci#include <linux/jhash.h> 4462306a36Sopenharmony_ci#include <linux/siphash.h> 4562306a36Sopenharmony_ci#include <net/net_namespace.h> 4662306a36Sopenharmony_ci#include <net/snmp.h> 4762306a36Sopenharmony_ci#include <net/ipv6.h> 4862306a36Sopenharmony_ci#include <net/ip6_fib.h> 4962306a36Sopenharmony_ci#include <net/ip6_route.h> 5062306a36Sopenharmony_ci#include <net/ndisc.h> 5162306a36Sopenharmony_ci#include <net/addrconf.h> 5262306a36Sopenharmony_ci#include <net/tcp.h> 5362306a36Sopenharmony_ci#include <linux/rtnetlink.h> 5462306a36Sopenharmony_ci#include <net/dst.h> 5562306a36Sopenharmony_ci#include <net/dst_metadata.h> 5662306a36Sopenharmony_ci#include <net/xfrm.h> 5762306a36Sopenharmony_ci#include <net/netevent.h> 5862306a36Sopenharmony_ci#include <net/netlink.h> 5962306a36Sopenharmony_ci#include <net/rtnh.h> 6062306a36Sopenharmony_ci#include <net/lwtunnel.h> 6162306a36Sopenharmony_ci#include <net/ip_tunnels.h> 6262306a36Sopenharmony_ci#include <net/l3mdev.h> 6362306a36Sopenharmony_ci#include <net/ip.h> 6462306a36Sopenharmony_ci#include <linux/uaccess.h> 6562306a36Sopenharmony_ci#include <linux/btf_ids.h> 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 6862306a36Sopenharmony_ci#include <linux/sysctl.h> 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 7462306a36Sopenharmony_ci#include <trace/events/fib6.h> 7562306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup); 7662306a36Sopenharmony_ci#undef CREATE_TRACE_POINTS 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cienum rt6_nud_state { 7962306a36Sopenharmony_ci RT6_NUD_FAIL_HARD = -3, 8062306a36Sopenharmony_ci RT6_NUD_FAIL_PROBE = -2, 8162306a36Sopenharmony_ci RT6_NUD_FAIL_DO_RR = -1, 8262306a36Sopenharmony_ci RT6_NUD_SUCCEED = 1 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE 8662306a36Sopenharmony_cistruct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 8762306a36Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst); 8862306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE 8962306a36Sopenharmony_ciunsigned int ip6_mtu(const struct dst_entry *dst); 9062306a36Sopenharmony_cistatic struct dst_entry *ip6_negative_advice(struct dst_entry *); 9162306a36Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *); 9262306a36Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *, 9362306a36Sopenharmony_ci struct net_device *dev); 9462306a36Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb); 9762306a36Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); 9862306a36Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb); 9962306a36Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); 10062306a36Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb); 10162306a36Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 10262306a36Sopenharmony_ci struct sk_buff *skb, u32 mtu, 10362306a36Sopenharmony_ci bool confirm_neigh); 10462306a36Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 10562306a36Sopenharmony_ci struct sk_buff *skb); 10662306a36Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif, 10762306a36Sopenharmony_ci int strict); 10862306a36Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i); 10962306a36Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb, 11062306a36Sopenharmony_ci struct fib6_info *rt, struct dst_entry *dst, 11162306a36Sopenharmony_ci struct in6_addr *dest, struct in6_addr *src, 11262306a36Sopenharmony_ci int iif, int type, u32 portid, u32 seq, 11362306a36Sopenharmony_ci unsigned int flags); 11462306a36Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 11562306a36Sopenharmony_ci const struct in6_addr *daddr, 11662306a36Sopenharmony_ci const struct in6_addr *saddr); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 11962306a36Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net, 12062306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 12162306a36Sopenharmony_ci const struct in6_addr *gwaddr, 12262306a36Sopenharmony_ci struct net_device *dev, 12362306a36Sopenharmony_ci unsigned int pref); 12462306a36Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net, 12562306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 12662306a36Sopenharmony_ci const struct in6_addr *gwaddr, 12762306a36Sopenharmony_ci struct net_device *dev); 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct uncached_list { 13162306a36Sopenharmony_ci spinlock_t lock; 13262306a36Sopenharmony_ci struct list_head head; 13362306a36Sopenharmony_ci struct list_head quarantine; 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid rt6_uncached_list_add(struct rt6_info *rt) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci rt->dst.rt_uncached_list = ul; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci spin_lock_bh(&ul->lock); 14562306a36Sopenharmony_ci list_add_tail(&rt->dst.rt_uncached, &ul->head); 14662306a36Sopenharmony_ci spin_unlock_bh(&ul->lock); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_civoid rt6_uncached_list_del(struct rt6_info *rt) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci if (!list_empty(&rt->dst.rt_uncached)) { 15262306a36Sopenharmony_ci struct uncached_list *ul = rt->dst.rt_uncached_list; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci spin_lock_bh(&ul->lock); 15562306a36Sopenharmony_ci list_del_init(&rt->dst.rt_uncached); 15662306a36Sopenharmony_ci spin_unlock_bh(&ul->lock); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void rt6_uncached_list_flush_dev(struct net_device *dev) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int cpu; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 16562306a36Sopenharmony_ci struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 16662306a36Sopenharmony_ci struct rt6_info *rt, *safe; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (list_empty(&ul->head)) 16962306a36Sopenharmony_ci continue; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci spin_lock_bh(&ul->lock); 17262306a36Sopenharmony_ci list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) { 17362306a36Sopenharmony_ci struct inet6_dev *rt_idev = rt->rt6i_idev; 17462306a36Sopenharmony_ci struct net_device *rt_dev = rt->dst.dev; 17562306a36Sopenharmony_ci bool handled = false; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (rt_idev->dev == dev) { 17862306a36Sopenharmony_ci rt->rt6i_idev = in6_dev_get(blackhole_netdev); 17962306a36Sopenharmony_ci in6_dev_put(rt_idev); 18062306a36Sopenharmony_ci handled = true; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (rt_dev == dev) { 18462306a36Sopenharmony_ci rt->dst.dev = blackhole_netdev; 18562306a36Sopenharmony_ci netdev_ref_replace(rt_dev, blackhole_netdev, 18662306a36Sopenharmony_ci &rt->dst.dev_tracker, 18762306a36Sopenharmony_ci GFP_ATOMIC); 18862306a36Sopenharmony_ci handled = true; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci if (handled) 19162306a36Sopenharmony_ci list_move(&rt->dst.rt_uncached, 19262306a36Sopenharmony_ci &ul->quarantine); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci spin_unlock_bh(&ul->lock); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic inline const void *choose_neigh_daddr(const struct in6_addr *p, 19962306a36Sopenharmony_ci struct sk_buff *skb, 20062306a36Sopenharmony_ci const void *daddr) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci if (!ipv6_addr_any(p)) 20362306a36Sopenharmony_ci return (const void *) p; 20462306a36Sopenharmony_ci else if (skb) 20562306a36Sopenharmony_ci return &ipv6_hdr(skb)->daddr; 20662306a36Sopenharmony_ci return daddr; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistruct neighbour *ip6_neigh_lookup(const struct in6_addr *gw, 21062306a36Sopenharmony_ci struct net_device *dev, 21162306a36Sopenharmony_ci struct sk_buff *skb, 21262306a36Sopenharmony_ci const void *daddr) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct neighbour *n; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci daddr = choose_neigh_daddr(gw, skb, daddr); 21762306a36Sopenharmony_ci n = __ipv6_neigh_lookup(dev, daddr); 21862306a36Sopenharmony_ci if (n) 21962306a36Sopenharmony_ci return n; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci n = neigh_create(&nd_tbl, daddr, dev); 22262306a36Sopenharmony_ci return IS_ERR(n) ? NULL : n; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst, 22662306a36Sopenharmony_ci struct sk_buff *skb, 22762306a36Sopenharmony_ci const void *daddr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci const struct rt6_info *rt = container_of(dst, struct rt6_info, dst); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any), 23262306a36Sopenharmony_ci dst->dev, skb, daddr); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct net_device *dev = dst->dev; 23862306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr); 24162306a36Sopenharmony_ci if (!daddr) 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci if (ipv6_addr_is_multicast((const struct in6_addr *)daddr)) 24662306a36Sopenharmony_ci return; 24762306a36Sopenharmony_ci __ipv6_confirm_neigh(dev, daddr); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct dst_ops ip6_dst_ops_template = { 25162306a36Sopenharmony_ci .family = AF_INET6, 25262306a36Sopenharmony_ci .gc = ip6_dst_gc, 25362306a36Sopenharmony_ci .gc_thresh = 1024, 25462306a36Sopenharmony_ci .check = ip6_dst_check, 25562306a36Sopenharmony_ci .default_advmss = ip6_default_advmss, 25662306a36Sopenharmony_ci .mtu = ip6_mtu, 25762306a36Sopenharmony_ci .cow_metrics = dst_cow_metrics_generic, 25862306a36Sopenharmony_ci .destroy = ip6_dst_destroy, 25962306a36Sopenharmony_ci .ifdown = ip6_dst_ifdown, 26062306a36Sopenharmony_ci .negative_advice = ip6_negative_advice, 26162306a36Sopenharmony_ci .link_failure = ip6_link_failure, 26262306a36Sopenharmony_ci .update_pmtu = ip6_rt_update_pmtu, 26362306a36Sopenharmony_ci .redirect = rt6_do_redirect, 26462306a36Sopenharmony_ci .local_out = __ip6_local_out, 26562306a36Sopenharmony_ci .neigh_lookup = ip6_dst_neigh_lookup, 26662306a36Sopenharmony_ci .confirm_neigh = ip6_confirm_neigh, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct dst_ops ip6_dst_blackhole_ops = { 27062306a36Sopenharmony_ci .family = AF_INET6, 27162306a36Sopenharmony_ci .default_advmss = ip6_default_advmss, 27262306a36Sopenharmony_ci .neigh_lookup = ip6_dst_neigh_lookup, 27362306a36Sopenharmony_ci .check = ip6_dst_check, 27462306a36Sopenharmony_ci .destroy = ip6_dst_destroy, 27562306a36Sopenharmony_ci .cow_metrics = dst_cow_metrics_generic, 27662306a36Sopenharmony_ci .update_pmtu = dst_blackhole_update_pmtu, 27762306a36Sopenharmony_ci .redirect = dst_blackhole_redirect, 27862306a36Sopenharmony_ci .mtu = dst_blackhole_mtu, 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const u32 ip6_template_metrics[RTAX_MAX] = { 28262306a36Sopenharmony_ci [RTAX_HOPLIMIT - 1] = 0, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct fib6_info fib6_null_entry_template = { 28662306a36Sopenharmony_ci .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP), 28762306a36Sopenharmony_ci .fib6_protocol = RTPROT_KERNEL, 28862306a36Sopenharmony_ci .fib6_metric = ~(u32)0, 28962306a36Sopenharmony_ci .fib6_ref = REFCOUNT_INIT(1), 29062306a36Sopenharmony_ci .fib6_type = RTN_UNREACHABLE, 29162306a36Sopenharmony_ci .fib6_metrics = (struct dst_metrics *)&dst_default_metrics, 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct rt6_info ip6_null_entry_template = { 29562306a36Sopenharmony_ci .dst = { 29662306a36Sopenharmony_ci .__rcuref = RCUREF_INIT(1), 29762306a36Sopenharmony_ci .__use = 1, 29862306a36Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 29962306a36Sopenharmony_ci .error = -ENETUNREACH, 30062306a36Sopenharmony_ci .input = ip6_pkt_discard, 30162306a36Sopenharmony_ci .output = ip6_pkt_discard_out, 30262306a36Sopenharmony_ci }, 30362306a36Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const struct rt6_info ip6_prohibit_entry_template = { 30962306a36Sopenharmony_ci .dst = { 31062306a36Sopenharmony_ci .__rcuref = RCUREF_INIT(1), 31162306a36Sopenharmony_ci .__use = 1, 31262306a36Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 31362306a36Sopenharmony_ci .error = -EACCES, 31462306a36Sopenharmony_ci .input = ip6_pkt_prohibit, 31562306a36Sopenharmony_ci .output = ip6_pkt_prohibit_out, 31662306a36Sopenharmony_ci }, 31762306a36Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic const struct rt6_info ip6_blk_hole_entry_template = { 32162306a36Sopenharmony_ci .dst = { 32262306a36Sopenharmony_ci .__rcuref = RCUREF_INIT(1), 32362306a36Sopenharmony_ci .__use = 1, 32462306a36Sopenharmony_ci .obsolete = DST_OBSOLETE_FORCE_CHK, 32562306a36Sopenharmony_ci .error = -EINVAL, 32662306a36Sopenharmony_ci .input = dst_discard, 32762306a36Sopenharmony_ci .output = dst_discard_out, 32862306a36Sopenharmony_ci }, 32962306a36Sopenharmony_ci .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci#endif 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void rt6_info_init(struct rt6_info *rt) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci memset_after(rt, 0, dst); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* allocate dst with ip6_dst_ops */ 34062306a36Sopenharmony_cistruct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, 34162306a36Sopenharmony_ci int flags) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 34462306a36Sopenharmony_ci 1, DST_OBSOLETE_FORCE_CHK, flags); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (rt) { 34762306a36Sopenharmony_ci rt6_info_init(rt); 34862306a36Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return rt; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ciEXPORT_SYMBOL(ip6_dst_alloc); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void ip6_dst_destroy(struct dst_entry *dst) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 35862306a36Sopenharmony_ci struct fib6_info *from; 35962306a36Sopenharmony_ci struct inet6_dev *idev; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ip_dst_metrics_put(dst); 36262306a36Sopenharmony_ci rt6_uncached_list_del(rt); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci idev = rt->rt6i_idev; 36562306a36Sopenharmony_ci if (idev) { 36662306a36Sopenharmony_ci rt->rt6i_idev = NULL; 36762306a36Sopenharmony_ci in6_dev_put(idev); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci from = xchg((__force struct fib6_info **)&rt->from, NULL); 37162306a36Sopenharmony_ci fib6_info_release(from); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)dst; 37762306a36Sopenharmony_ci struct inet6_dev *idev = rt->rt6i_idev; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (idev && idev->dev != blackhole_netdev) { 38062306a36Sopenharmony_ci struct inet6_dev *blackhole_idev = in6_dev_get(blackhole_netdev); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (blackhole_idev) { 38362306a36Sopenharmony_ci rt->rt6i_idev = blackhole_idev; 38462306a36Sopenharmony_ci in6_dev_put(idev); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic bool __rt6_check_expired(const struct rt6_info *rt) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_EXPIRES) 39262306a36Sopenharmony_ci return time_after(jiffies, rt->dst.expires); 39362306a36Sopenharmony_ci else 39462306a36Sopenharmony_ci return false; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic bool rt6_check_expired(const struct rt6_info *rt) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct fib6_info *from; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci from = rcu_dereference(rt->from); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_EXPIRES) { 40462306a36Sopenharmony_ci if (time_after(jiffies, rt->dst.expires)) 40562306a36Sopenharmony_ci return true; 40662306a36Sopenharmony_ci } else if (from) { 40762306a36Sopenharmony_ci return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK || 40862306a36Sopenharmony_ci fib6_check_expired(from); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci return false; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_civoid fib6_select_path(const struct net *net, struct fib6_result *res, 41462306a36Sopenharmony_ci struct flowi6 *fl6, int oif, bool have_oif_match, 41562306a36Sopenharmony_ci const struct sk_buff *skb, int strict) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 41862306a36Sopenharmony_ci struct fib6_info *match = res->f6i; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!match->nh && (!match->fib6_nsiblings || have_oif_match)) 42162306a36Sopenharmony_ci goto out; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (match->nh && have_oif_match && res->nh) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (skb) 42762306a36Sopenharmony_ci IP6CB(skb)->flags |= IP6SKB_MULTIPATH; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* We might have already computed the hash for ICMPv6 errors. In such 43062306a36Sopenharmony_ci * case it will always be non-zero. Otherwise now is the time to do it. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci if (!fl6->mp_hash && 43362306a36Sopenharmony_ci (!match->nh || nexthop_is_multipath(match->nh))) 43462306a36Sopenharmony_ci fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (unlikely(match->nh)) { 43762306a36Sopenharmony_ci nexthop_path_fib6_result(res, fl6->mp_hash); 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound)) 44262306a36Sopenharmony_ci goto out; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings, 44562306a36Sopenharmony_ci fib6_siblings) { 44662306a36Sopenharmony_ci const struct fib6_nh *nh = sibling->fib6_nh; 44762306a36Sopenharmony_ci int nh_upper_bound; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound); 45062306a36Sopenharmony_ci if (fl6->mp_hash > nh_upper_bound) 45162306a36Sopenharmony_ci continue; 45262306a36Sopenharmony_ci if (rt6_score_route(nh, sibling->fib6_flags, oif, strict) < 0) 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci match = sibling; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciout: 45962306a36Sopenharmony_ci res->f6i = match; 46062306a36Sopenharmony_ci res->nh = match->fib6_nh; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/* 46462306a36Sopenharmony_ci * Route lookup. rcu_read_lock() should be held. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic bool __rt6_device_match(struct net *net, const struct fib6_nh *nh, 46862306a36Sopenharmony_ci const struct in6_addr *saddr, int oif, int flags) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci const struct net_device *dev; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) 47362306a36Sopenharmony_ci return false; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci dev = nh->fib_nh_dev; 47662306a36Sopenharmony_ci if (oif) { 47762306a36Sopenharmony_ci if (dev->ifindex == oif) 47862306a36Sopenharmony_ci return true; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci if (ipv6_chk_addr(net, saddr, dev, 48162306a36Sopenharmony_ci flags & RT6_LOOKUP_F_IFACE)) 48262306a36Sopenharmony_ci return true; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistruct fib6_nh_dm_arg { 48962306a36Sopenharmony_ci struct net *net; 49062306a36Sopenharmony_ci const struct in6_addr *saddr; 49162306a36Sopenharmony_ci int oif; 49262306a36Sopenharmony_ci int flags; 49362306a36Sopenharmony_ci struct fib6_nh *nh; 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int __rt6_nh_dev_match(struct fib6_nh *nh, void *_arg) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct fib6_nh_dm_arg *arg = _arg; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci arg->nh = nh; 50162306a36Sopenharmony_ci return __rt6_device_match(arg->net, nh, arg->saddr, arg->oif, 50262306a36Sopenharmony_ci arg->flags); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* returns fib6_nh from nexthop or NULL */ 50662306a36Sopenharmony_cistatic struct fib6_nh *rt6_nh_dev_match(struct net *net, struct nexthop *nh, 50762306a36Sopenharmony_ci struct fib6_result *res, 50862306a36Sopenharmony_ci const struct in6_addr *saddr, 50962306a36Sopenharmony_ci int oif, int flags) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct fib6_nh_dm_arg arg = { 51262306a36Sopenharmony_ci .net = net, 51362306a36Sopenharmony_ci .saddr = saddr, 51462306a36Sopenharmony_ci .oif = oif, 51562306a36Sopenharmony_ci .flags = flags, 51662306a36Sopenharmony_ci }; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (nexthop_is_blackhole(nh)) 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (nexthop_for_each_fib6_nh(nh, __rt6_nh_dev_match, &arg)) 52262306a36Sopenharmony_ci return arg.nh; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return NULL; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void rt6_device_match(struct net *net, struct fib6_result *res, 52862306a36Sopenharmony_ci const struct in6_addr *saddr, int oif, int flags) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 53162306a36Sopenharmony_ci struct fib6_info *spf6i; 53262306a36Sopenharmony_ci struct fib6_nh *nh; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!oif && ipv6_addr_any(saddr)) { 53562306a36Sopenharmony_ci if (unlikely(f6i->nh)) { 53662306a36Sopenharmony_ci nh = nexthop_fib6_nh(f6i->nh); 53762306a36Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) 53862306a36Sopenharmony_ci goto out_blackhole; 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci nh = f6i->fib6_nh; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci if (!(nh->fib_nh_flags & RTNH_F_DEAD)) 54362306a36Sopenharmony_ci goto out; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci for (spf6i = f6i; spf6i; spf6i = rcu_dereference(spf6i->fib6_next)) { 54762306a36Sopenharmony_ci bool matched = false; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (unlikely(spf6i->nh)) { 55062306a36Sopenharmony_ci nh = rt6_nh_dev_match(net, spf6i->nh, res, saddr, 55162306a36Sopenharmony_ci oif, flags); 55262306a36Sopenharmony_ci if (nh) 55362306a36Sopenharmony_ci matched = true; 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci nh = spf6i->fib6_nh; 55662306a36Sopenharmony_ci if (__rt6_device_match(net, nh, saddr, oif, flags)) 55762306a36Sopenharmony_ci matched = true; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (matched) { 56062306a36Sopenharmony_ci res->f6i = spf6i; 56162306a36Sopenharmony_ci goto out; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (oif && flags & RT6_LOOKUP_F_IFACE) { 56662306a36Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 56762306a36Sopenharmony_ci nh = res->f6i->fib6_nh; 56862306a36Sopenharmony_ci goto out; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (unlikely(f6i->nh)) { 57262306a36Sopenharmony_ci nh = nexthop_fib6_nh(f6i->nh); 57362306a36Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) 57462306a36Sopenharmony_ci goto out_blackhole; 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci nh = f6i->fib6_nh; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) { 58062306a36Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 58162306a36Sopenharmony_ci nh = res->f6i->fib6_nh; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ciout: 58462306a36Sopenharmony_ci res->nh = nh; 58562306a36Sopenharmony_ci res->fib6_type = res->f6i->fib6_type; 58662306a36Sopenharmony_ci res->fib6_flags = res->f6i->fib6_flags; 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciout_blackhole: 59062306a36Sopenharmony_ci res->fib6_flags |= RTF_REJECT; 59162306a36Sopenharmony_ci res->fib6_type = RTN_BLACKHOLE; 59262306a36Sopenharmony_ci res->nh = nh; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 59662306a36Sopenharmony_cistruct __rt6_probe_work { 59762306a36Sopenharmony_ci struct work_struct work; 59862306a36Sopenharmony_ci struct in6_addr target; 59962306a36Sopenharmony_ci struct net_device *dev; 60062306a36Sopenharmony_ci netdevice_tracker dev_tracker; 60162306a36Sopenharmony_ci}; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void rt6_probe_deferred(struct work_struct *w) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct in6_addr mcaddr; 60662306a36Sopenharmony_ci struct __rt6_probe_work *work = 60762306a36Sopenharmony_ci container_of(w, struct __rt6_probe_work, work); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci addrconf_addr_solict_mult(&work->target, &mcaddr); 61062306a36Sopenharmony_ci ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); 61162306a36Sopenharmony_ci netdev_put(work->dev, &work->dev_tracker); 61262306a36Sopenharmony_ci kfree(work); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic void rt6_probe(struct fib6_nh *fib6_nh) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct __rt6_probe_work *work = NULL; 61862306a36Sopenharmony_ci const struct in6_addr *nh_gw; 61962306a36Sopenharmony_ci unsigned long last_probe; 62062306a36Sopenharmony_ci struct neighbour *neigh; 62162306a36Sopenharmony_ci struct net_device *dev; 62262306a36Sopenharmony_ci struct inet6_dev *idev; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * Okay, this does not seem to be appropriate 62662306a36Sopenharmony_ci * for now, however, we need to check if it 62762306a36Sopenharmony_ci * is really so; aka Router Reachability Probing. 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci * Router Reachability Probe MUST be rate-limited 63062306a36Sopenharmony_ci * to no more than one per minute. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci if (!fib6_nh->fib_nh_gw_family) 63362306a36Sopenharmony_ci return; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci nh_gw = &fib6_nh->fib_nh_gw6; 63662306a36Sopenharmony_ci dev = fib6_nh->fib_nh_dev; 63762306a36Sopenharmony_ci rcu_read_lock(); 63862306a36Sopenharmony_ci last_probe = READ_ONCE(fib6_nh->last_probe); 63962306a36Sopenharmony_ci idev = __in6_dev_get(dev); 64062306a36Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(dev, nh_gw); 64162306a36Sopenharmony_ci if (neigh) { 64262306a36Sopenharmony_ci if (READ_ONCE(neigh->nud_state) & NUD_VALID) 64362306a36Sopenharmony_ci goto out; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci write_lock_bh(&neigh->lock); 64662306a36Sopenharmony_ci if (!(neigh->nud_state & NUD_VALID) && 64762306a36Sopenharmony_ci time_after(jiffies, 64862306a36Sopenharmony_ci neigh->updated + idev->cnf.rtr_probe_interval)) { 64962306a36Sopenharmony_ci work = kmalloc(sizeof(*work), GFP_ATOMIC); 65062306a36Sopenharmony_ci if (work) 65162306a36Sopenharmony_ci __neigh_set_probe_once(neigh); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci write_unlock_bh(&neigh->lock); 65462306a36Sopenharmony_ci } else if (time_after(jiffies, last_probe + 65562306a36Sopenharmony_ci idev->cnf.rtr_probe_interval)) { 65662306a36Sopenharmony_ci work = kmalloc(sizeof(*work), GFP_ATOMIC); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (!work || cmpxchg(&fib6_nh->last_probe, 66062306a36Sopenharmony_ci last_probe, jiffies) != last_probe) { 66162306a36Sopenharmony_ci kfree(work); 66262306a36Sopenharmony_ci } else { 66362306a36Sopenharmony_ci INIT_WORK(&work->work, rt6_probe_deferred); 66462306a36Sopenharmony_ci work->target = *nh_gw; 66562306a36Sopenharmony_ci netdev_hold(dev, &work->dev_tracker, GFP_ATOMIC); 66662306a36Sopenharmony_ci work->dev = dev; 66762306a36Sopenharmony_ci schedule_work(&work->work); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciout: 67162306a36Sopenharmony_ci rcu_read_unlock(); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci#else 67462306a36Sopenharmony_cistatic inline void rt6_probe(struct fib6_nh *fib6_nh) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci#endif 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* 68062306a36Sopenharmony_ci * Default Router Selection (RFC 2461 6.3.6) 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_cistatic enum rt6_nud_state rt6_check_neigh(const struct fib6_nh *fib6_nh) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 68562306a36Sopenharmony_ci struct neighbour *neigh; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci rcu_read_lock(); 68862306a36Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(fib6_nh->fib_nh_dev, 68962306a36Sopenharmony_ci &fib6_nh->fib_nh_gw6); 69062306a36Sopenharmony_ci if (neigh) { 69162306a36Sopenharmony_ci u8 nud_state = READ_ONCE(neigh->nud_state); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (nud_state & NUD_VALID) 69462306a36Sopenharmony_ci ret = RT6_NUD_SUCCEED; 69562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 69662306a36Sopenharmony_ci else if (!(nud_state & NUD_FAILED)) 69762306a36Sopenharmony_ci ret = RT6_NUD_SUCCEED; 69862306a36Sopenharmony_ci else 69962306a36Sopenharmony_ci ret = RT6_NUD_FAIL_PROBE; 70062306a36Sopenharmony_ci#endif 70162306a36Sopenharmony_ci } else { 70262306a36Sopenharmony_ci ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 70362306a36Sopenharmony_ci RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci rcu_read_unlock(); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return ret; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif, 71162306a36Sopenharmony_ci int strict) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci int m = 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (!oif || nh->fib_nh_dev->ifindex == oif) 71662306a36Sopenharmony_ci m = 2; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!m && (strict & RT6_LOOKUP_F_IFACE)) 71962306a36Sopenharmony_ci return RT6_NUD_FAIL_HARD; 72062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 72162306a36Sopenharmony_ci m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(fib6_flags)) << 2; 72262306a36Sopenharmony_ci#endif 72362306a36Sopenharmony_ci if ((strict & RT6_LOOKUP_F_REACHABLE) && 72462306a36Sopenharmony_ci !(fib6_flags & RTF_NONEXTHOP) && nh->fib_nh_gw_family) { 72562306a36Sopenharmony_ci int n = rt6_check_neigh(nh); 72662306a36Sopenharmony_ci if (n < 0) 72762306a36Sopenharmony_ci return n; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return m; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic bool find_match(struct fib6_nh *nh, u32 fib6_flags, 73362306a36Sopenharmony_ci int oif, int strict, int *mpri, bool *do_rr) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci bool match_do_rr = false; 73662306a36Sopenharmony_ci bool rc = false; 73762306a36Sopenharmony_ci int m; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD) 74062306a36Sopenharmony_ci goto out; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (ip6_ignore_linkdown(nh->fib_nh_dev) && 74362306a36Sopenharmony_ci nh->fib_nh_flags & RTNH_F_LINKDOWN && 74462306a36Sopenharmony_ci !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) 74562306a36Sopenharmony_ci goto out; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci m = rt6_score_route(nh, fib6_flags, oif, strict); 74862306a36Sopenharmony_ci if (m == RT6_NUD_FAIL_DO_RR) { 74962306a36Sopenharmony_ci match_do_rr = true; 75062306a36Sopenharmony_ci m = 0; /* lowest valid score */ 75162306a36Sopenharmony_ci } else if (m == RT6_NUD_FAIL_HARD) { 75262306a36Sopenharmony_ci goto out; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (strict & RT6_LOOKUP_F_REACHABLE) 75662306a36Sopenharmony_ci rt6_probe(nh); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 75962306a36Sopenharmony_ci if (m > *mpri) { 76062306a36Sopenharmony_ci *do_rr = match_do_rr; 76162306a36Sopenharmony_ci *mpri = m; 76262306a36Sopenharmony_ci rc = true; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ciout: 76562306a36Sopenharmony_ci return rc; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistruct fib6_nh_frl_arg { 76962306a36Sopenharmony_ci u32 flags; 77062306a36Sopenharmony_ci int oif; 77162306a36Sopenharmony_ci int strict; 77262306a36Sopenharmony_ci int *mpri; 77362306a36Sopenharmony_ci bool *do_rr; 77462306a36Sopenharmony_ci struct fib6_nh *nh; 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int rt6_nh_find_match(struct fib6_nh *nh, void *_arg) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct fib6_nh_frl_arg *arg = _arg; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci arg->nh = nh; 78262306a36Sopenharmony_ci return find_match(nh, arg->flags, arg->oif, arg->strict, 78362306a36Sopenharmony_ci arg->mpri, arg->do_rr); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void __find_rr_leaf(struct fib6_info *f6i_start, 78762306a36Sopenharmony_ci struct fib6_info *nomatch, u32 metric, 78862306a36Sopenharmony_ci struct fib6_result *res, struct fib6_info **cont, 78962306a36Sopenharmony_ci int oif, int strict, bool *do_rr, int *mpri) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct fib6_info *f6i; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci for (f6i = f6i_start; 79462306a36Sopenharmony_ci f6i && f6i != nomatch; 79562306a36Sopenharmony_ci f6i = rcu_dereference(f6i->fib6_next)) { 79662306a36Sopenharmony_ci bool matched = false; 79762306a36Sopenharmony_ci struct fib6_nh *nh; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (cont && f6i->fib6_metric != metric) { 80062306a36Sopenharmony_ci *cont = f6i; 80162306a36Sopenharmony_ci return; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (fib6_check_expired(f6i)) 80562306a36Sopenharmony_ci continue; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (unlikely(f6i->nh)) { 80862306a36Sopenharmony_ci struct fib6_nh_frl_arg arg = { 80962306a36Sopenharmony_ci .flags = f6i->fib6_flags, 81062306a36Sopenharmony_ci .oif = oif, 81162306a36Sopenharmony_ci .strict = strict, 81262306a36Sopenharmony_ci .mpri = mpri, 81362306a36Sopenharmony_ci .do_rr = do_rr 81462306a36Sopenharmony_ci }; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (nexthop_is_blackhole(f6i->nh)) { 81762306a36Sopenharmony_ci res->fib6_flags = RTF_REJECT; 81862306a36Sopenharmony_ci res->fib6_type = RTN_BLACKHOLE; 81962306a36Sopenharmony_ci res->f6i = f6i; 82062306a36Sopenharmony_ci res->nh = nexthop_fib6_nh(f6i->nh); 82162306a36Sopenharmony_ci return; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match, 82462306a36Sopenharmony_ci &arg)) { 82562306a36Sopenharmony_ci matched = true; 82662306a36Sopenharmony_ci nh = arg.nh; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci nh = f6i->fib6_nh; 83062306a36Sopenharmony_ci if (find_match(nh, f6i->fib6_flags, oif, strict, 83162306a36Sopenharmony_ci mpri, do_rr)) 83262306a36Sopenharmony_ci matched = true; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci if (matched) { 83562306a36Sopenharmony_ci res->f6i = f6i; 83662306a36Sopenharmony_ci res->nh = nh; 83762306a36Sopenharmony_ci res->fib6_flags = f6i->fib6_flags; 83862306a36Sopenharmony_ci res->fib6_type = f6i->fib6_type; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic void find_rr_leaf(struct fib6_node *fn, struct fib6_info *leaf, 84462306a36Sopenharmony_ci struct fib6_info *rr_head, int oif, int strict, 84562306a36Sopenharmony_ci bool *do_rr, struct fib6_result *res) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci u32 metric = rr_head->fib6_metric; 84862306a36Sopenharmony_ci struct fib6_info *cont = NULL; 84962306a36Sopenharmony_ci int mpri = -1; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci __find_rr_leaf(rr_head, NULL, metric, res, &cont, 85262306a36Sopenharmony_ci oif, strict, do_rr, &mpri); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci __find_rr_leaf(leaf, rr_head, metric, res, &cont, 85562306a36Sopenharmony_ci oif, strict, do_rr, &mpri); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (res->f6i || !cont) 85862306a36Sopenharmony_ci return; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci __find_rr_leaf(cont, NULL, metric, res, NULL, 86162306a36Sopenharmony_ci oif, strict, do_rr, &mpri); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic void rt6_select(struct net *net, struct fib6_node *fn, int oif, 86562306a36Sopenharmony_ci struct fib6_result *res, int strict) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct fib6_info *leaf = rcu_dereference(fn->leaf); 86862306a36Sopenharmony_ci struct fib6_info *rt0; 86962306a36Sopenharmony_ci bool do_rr = false; 87062306a36Sopenharmony_ci int key_plen; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* make sure this function or its helpers sets f6i */ 87362306a36Sopenharmony_ci res->f6i = NULL; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (!leaf || leaf == net->ipv6.fib6_null_entry) 87662306a36Sopenharmony_ci goto out; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci rt0 = rcu_dereference(fn->rr_ptr); 87962306a36Sopenharmony_ci if (!rt0) 88062306a36Sopenharmony_ci rt0 = leaf; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* Double check to make sure fn is not an intermediate node 88362306a36Sopenharmony_ci * and fn->leaf does not points to its child's leaf 88462306a36Sopenharmony_ci * (This might happen if all routes under fn are deleted from 88562306a36Sopenharmony_ci * the tree and fib6_repair_tree() is called on the node.) 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_ci key_plen = rt0->fib6_dst.plen; 88862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 88962306a36Sopenharmony_ci if (rt0->fib6_src.plen) 89062306a36Sopenharmony_ci key_plen = rt0->fib6_src.plen; 89162306a36Sopenharmony_ci#endif 89262306a36Sopenharmony_ci if (fn->fn_bit != key_plen) 89362306a36Sopenharmony_ci goto out; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci find_rr_leaf(fn, leaf, rt0, oif, strict, &do_rr, res); 89662306a36Sopenharmony_ci if (do_rr) { 89762306a36Sopenharmony_ci struct fib6_info *next = rcu_dereference(rt0->fib6_next); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* no entries matched; do round-robin */ 90062306a36Sopenharmony_ci if (!next || next->fib6_metric != rt0->fib6_metric) 90162306a36Sopenharmony_ci next = leaf; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (next != rt0) { 90462306a36Sopenharmony_ci spin_lock_bh(&leaf->fib6_table->tb6_lock); 90562306a36Sopenharmony_ci /* make sure next is not being deleted from the tree */ 90662306a36Sopenharmony_ci if (next->fib6_node) 90762306a36Sopenharmony_ci rcu_assign_pointer(fn->rr_ptr, next); 90862306a36Sopenharmony_ci spin_unlock_bh(&leaf->fib6_table->tb6_lock); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ciout: 91362306a36Sopenharmony_ci if (!res->f6i) { 91462306a36Sopenharmony_ci res->f6i = net->ipv6.fib6_null_entry; 91562306a36Sopenharmony_ci res->nh = res->f6i->fib6_nh; 91662306a36Sopenharmony_ci res->fib6_flags = res->f6i->fib6_flags; 91762306a36Sopenharmony_ci res->fib6_type = res->f6i->fib6_type; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic bool rt6_is_gw_or_nonexthop(const struct fib6_result *res) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci return (res->f6i->fib6_flags & RTF_NONEXTHOP) || 92462306a36Sopenharmony_ci res->nh->fib_nh_gw_family; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 92862306a36Sopenharmony_ciint rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 92962306a36Sopenharmony_ci const struct in6_addr *gwaddr) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct net *net = dev_net(dev); 93262306a36Sopenharmony_ci struct route_info *rinfo = (struct route_info *) opt; 93362306a36Sopenharmony_ci struct in6_addr prefix_buf, *prefix; 93462306a36Sopenharmony_ci unsigned int pref; 93562306a36Sopenharmony_ci unsigned long lifetime; 93662306a36Sopenharmony_ci struct fib6_info *rt; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (len < sizeof(struct route_info)) { 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Sanity check for prefix_len and length */ 94362306a36Sopenharmony_ci if (rinfo->length > 3) { 94462306a36Sopenharmony_ci return -EINVAL; 94562306a36Sopenharmony_ci } else if (rinfo->prefix_len > 128) { 94662306a36Sopenharmony_ci return -EINVAL; 94762306a36Sopenharmony_ci } else if (rinfo->prefix_len > 64) { 94862306a36Sopenharmony_ci if (rinfo->length < 2) { 94962306a36Sopenharmony_ci return -EINVAL; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci } else if (rinfo->prefix_len > 0) { 95262306a36Sopenharmony_ci if (rinfo->length < 1) { 95362306a36Sopenharmony_ci return -EINVAL; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci pref = rinfo->route_pref; 95862306a36Sopenharmony_ci if (pref == ICMPV6_ROUTER_PREF_INVALID) 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (rinfo->length == 3) 96462306a36Sopenharmony_ci prefix = (struct in6_addr *)rinfo->prefix; 96562306a36Sopenharmony_ci else { 96662306a36Sopenharmony_ci /* this function is safe */ 96762306a36Sopenharmony_ci ipv6_addr_prefix(&prefix_buf, 96862306a36Sopenharmony_ci (struct in6_addr *)rinfo->prefix, 96962306a36Sopenharmony_ci rinfo->prefix_len); 97062306a36Sopenharmony_ci prefix = &prefix_buf; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (rinfo->prefix_len == 0) 97462306a36Sopenharmony_ci rt = rt6_get_dflt_router(net, gwaddr, dev); 97562306a36Sopenharmony_ci else 97662306a36Sopenharmony_ci rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 97762306a36Sopenharmony_ci gwaddr, dev); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (rt && !lifetime) { 98062306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 98162306a36Sopenharmony_ci rt = NULL; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (!rt && lifetime) 98562306a36Sopenharmony_ci rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, 98662306a36Sopenharmony_ci dev, pref); 98762306a36Sopenharmony_ci else if (rt) 98862306a36Sopenharmony_ci rt->fib6_flags = RTF_ROUTEINFO | 98962306a36Sopenharmony_ci (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (rt) { 99262306a36Sopenharmony_ci if (!addrconf_finite_timeout(lifetime)) 99362306a36Sopenharmony_ci fib6_clean_expires(rt); 99462306a36Sopenharmony_ci else 99562306a36Sopenharmony_ci fib6_set_expires(rt, jiffies + HZ * lifetime); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci fib6_info_release(rt); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci#endif 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/* 100462306a36Sopenharmony_ci * Misc support functions 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/* called with rcu_lock held */ 100862306a36Sopenharmony_cistatic struct net_device *ip6_rt_get_dev_rcu(const struct fib6_result *res) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct net_device *dev = res->nh->fib_nh_dev; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (res->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) { 101362306a36Sopenharmony_ci /* for copies of local routes, dst->dev needs to be the 101462306a36Sopenharmony_ci * device if it is a master device, the master device if 101562306a36Sopenharmony_ci * device is enslaved, and the loopback as the default 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_ci if (netif_is_l3_slave(dev) && 101862306a36Sopenharmony_ci !rt6_need_strict(&res->f6i->fib6_dst.addr)) 101962306a36Sopenharmony_ci dev = l3mdev_master_dev_rcu(dev); 102062306a36Sopenharmony_ci else if (!netif_is_l3_master(dev)) 102162306a36Sopenharmony_ci dev = dev_net(dev)->loopback_dev; 102262306a36Sopenharmony_ci /* last case is netif_is_l3_master(dev) is true in which 102362306a36Sopenharmony_ci * case we want dev returned to be dev 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return dev; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic const int fib6_prop[RTN_MAX + 1] = { 103162306a36Sopenharmony_ci [RTN_UNSPEC] = 0, 103262306a36Sopenharmony_ci [RTN_UNICAST] = 0, 103362306a36Sopenharmony_ci [RTN_LOCAL] = 0, 103462306a36Sopenharmony_ci [RTN_BROADCAST] = 0, 103562306a36Sopenharmony_ci [RTN_ANYCAST] = 0, 103662306a36Sopenharmony_ci [RTN_MULTICAST] = 0, 103762306a36Sopenharmony_ci [RTN_BLACKHOLE] = -EINVAL, 103862306a36Sopenharmony_ci [RTN_UNREACHABLE] = -EHOSTUNREACH, 103962306a36Sopenharmony_ci [RTN_PROHIBIT] = -EACCES, 104062306a36Sopenharmony_ci [RTN_THROW] = -EAGAIN, 104162306a36Sopenharmony_ci [RTN_NAT] = -EINVAL, 104262306a36Sopenharmony_ci [RTN_XRESOLVE] = -EINVAL, 104362306a36Sopenharmony_ci}; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int ip6_rt_type_to_error(u8 fib6_type) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci return fib6_prop[fib6_type]; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic unsigned short fib6_info_dst_flags(struct fib6_info *rt) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci unsigned short flags = 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (rt->dst_nocount) 105562306a36Sopenharmony_ci flags |= DST_NOCOUNT; 105662306a36Sopenharmony_ci if (rt->dst_nopolicy) 105762306a36Sopenharmony_ci flags |= DST_NOPOLICY; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return flags; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci rt->dst.error = ip6_rt_type_to_error(fib6_type); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci switch (fib6_type) { 106762306a36Sopenharmony_ci case RTN_BLACKHOLE: 106862306a36Sopenharmony_ci rt->dst.output = dst_discard_out; 106962306a36Sopenharmony_ci rt->dst.input = dst_discard; 107062306a36Sopenharmony_ci break; 107162306a36Sopenharmony_ci case RTN_PROHIBIT: 107262306a36Sopenharmony_ci rt->dst.output = ip6_pkt_prohibit_out; 107362306a36Sopenharmony_ci rt->dst.input = ip6_pkt_prohibit; 107462306a36Sopenharmony_ci break; 107562306a36Sopenharmony_ci case RTN_THROW: 107662306a36Sopenharmony_ci case RTN_UNREACHABLE: 107762306a36Sopenharmony_ci default: 107862306a36Sopenharmony_ci rt->dst.output = ip6_pkt_discard_out; 107962306a36Sopenharmony_ci rt->dst.input = ip6_pkt_discard; 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic void ip6_rt_init_dst(struct rt6_info *rt, const struct fib6_result *res) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (res->fib6_flags & RTF_REJECT) { 108962306a36Sopenharmony_ci ip6_rt_init_dst_reject(rt, res->fib6_type); 109062306a36Sopenharmony_ci return; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci rt->dst.error = 0; 109462306a36Sopenharmony_ci rt->dst.output = ip6_output; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (res->fib6_type == RTN_LOCAL || res->fib6_type == RTN_ANYCAST) { 109762306a36Sopenharmony_ci rt->dst.input = ip6_input; 109862306a36Sopenharmony_ci } else if (ipv6_addr_type(&f6i->fib6_dst.addr) & IPV6_ADDR_MULTICAST) { 109962306a36Sopenharmony_ci rt->dst.input = ip6_mc_input; 110062306a36Sopenharmony_ci } else { 110162306a36Sopenharmony_ci rt->dst.input = ip6_forward; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (res->nh->fib_nh_lws) { 110562306a36Sopenharmony_ci rt->dst.lwtstate = lwtstate_get(res->nh->fib_nh_lws); 110662306a36Sopenharmony_ci lwtunnel_set_redirect(&rt->dst); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci rt->dst.lastuse = jiffies; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/* Caller must already hold reference to @from */ 111362306a36Sopenharmony_cistatic void rt6_set_from(struct rt6_info *rt, struct fib6_info *from) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci rt->rt6i_flags &= ~RTF_EXPIRES; 111662306a36Sopenharmony_ci rcu_assign_pointer(rt->from, from); 111762306a36Sopenharmony_ci ip_dst_init_metrics(&rt->dst, from->fib6_metrics); 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci/* Caller must already hold reference to f6i in result */ 112162306a36Sopenharmony_cistatic void ip6_rt_copy_init(struct rt6_info *rt, const struct fib6_result *res) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci const struct fib6_nh *nh = res->nh; 112462306a36Sopenharmony_ci const struct net_device *dev = nh->fib_nh_dev; 112562306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci ip6_rt_init_dst(rt, res); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci rt->rt6i_dst = f6i->fib6_dst; 113062306a36Sopenharmony_ci rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL; 113162306a36Sopenharmony_ci rt->rt6i_flags = res->fib6_flags; 113262306a36Sopenharmony_ci if (nh->fib_nh_gw_family) { 113362306a36Sopenharmony_ci rt->rt6i_gateway = nh->fib_nh_gw6; 113462306a36Sopenharmony_ci rt->rt6i_flags |= RTF_GATEWAY; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci rt6_set_from(rt, f6i); 113762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 113862306a36Sopenharmony_ci rt->rt6i_src = f6i->fib6_src; 113962306a36Sopenharmony_ci#endif 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic struct fib6_node* fib6_backtrack(struct fib6_node *fn, 114362306a36Sopenharmony_ci struct in6_addr *saddr) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct fib6_node *pn, *sn; 114662306a36Sopenharmony_ci while (1) { 114762306a36Sopenharmony_ci if (fn->fn_flags & RTN_TL_ROOT) 114862306a36Sopenharmony_ci return NULL; 114962306a36Sopenharmony_ci pn = rcu_dereference(fn->parent); 115062306a36Sopenharmony_ci sn = FIB6_SUBTREE(pn); 115162306a36Sopenharmony_ci if (sn && sn != fn) 115262306a36Sopenharmony_ci fn = fib6_node_lookup(sn, NULL, saddr); 115362306a36Sopenharmony_ci else 115462306a36Sopenharmony_ci fn = pn; 115562306a36Sopenharmony_ci if (fn->fn_flags & RTN_RTINFO) 115662306a36Sopenharmony_ci return fn; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_cistatic bool ip6_hold_safe(struct net *net, struct rt6_info **prt) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct rt6_info *rt = *prt; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (dst_hold_safe(&rt->dst)) 116562306a36Sopenharmony_ci return true; 116662306a36Sopenharmony_ci if (net) { 116762306a36Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 116862306a36Sopenharmony_ci dst_hold(&rt->dst); 116962306a36Sopenharmony_ci } else { 117062306a36Sopenharmony_ci rt = NULL; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci *prt = rt; 117362306a36Sopenharmony_ci return false; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci/* called with rcu_lock held */ 117762306a36Sopenharmony_cistatic struct rt6_info *ip6_create_rt_rcu(const struct fib6_result *res) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct net_device *dev = res->nh->fib_nh_dev; 118062306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 118162306a36Sopenharmony_ci unsigned short flags; 118262306a36Sopenharmony_ci struct rt6_info *nrt; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 118562306a36Sopenharmony_ci goto fallback; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci flags = fib6_info_dst_flags(f6i); 118862306a36Sopenharmony_ci nrt = ip6_dst_alloc(dev_net(dev), dev, flags); 118962306a36Sopenharmony_ci if (!nrt) { 119062306a36Sopenharmony_ci fib6_info_release(f6i); 119162306a36Sopenharmony_ci goto fallback; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci ip6_rt_copy_init(nrt, res); 119562306a36Sopenharmony_ci return nrt; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cifallback: 119862306a36Sopenharmony_ci nrt = dev_net(dev)->ipv6.ip6_null_entry; 119962306a36Sopenharmony_ci dst_hold(&nrt->dst); 120062306a36Sopenharmony_ci return nrt; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_lookup(struct net *net, 120462306a36Sopenharmony_ci struct fib6_table *table, 120562306a36Sopenharmony_ci struct flowi6 *fl6, 120662306a36Sopenharmony_ci const struct sk_buff *skb, 120762306a36Sopenharmony_ci int flags) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct fib6_result res = {}; 121062306a36Sopenharmony_ci struct fib6_node *fn; 121162306a36Sopenharmony_ci struct rt6_info *rt; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci rcu_read_lock(); 121462306a36Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 121562306a36Sopenharmony_cirestart: 121662306a36Sopenharmony_ci res.f6i = rcu_dereference(fn->leaf); 121762306a36Sopenharmony_ci if (!res.f6i) 121862306a36Sopenharmony_ci res.f6i = net->ipv6.fib6_null_entry; 121962306a36Sopenharmony_ci else 122062306a36Sopenharmony_ci rt6_device_match(net, &res, &fl6->saddr, fl6->flowi6_oif, 122162306a36Sopenharmony_ci flags); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (res.f6i == net->ipv6.fib6_null_entry) { 122462306a36Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 122562306a36Sopenharmony_ci if (fn) 122662306a36Sopenharmony_ci goto restart; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 122962306a36Sopenharmony_ci dst_hold(&rt->dst); 123062306a36Sopenharmony_ci goto out; 123162306a36Sopenharmony_ci } else if (res.fib6_flags & RTF_REJECT) { 123262306a36Sopenharmony_ci goto do_create; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci fib6_select_path(net, &res, fl6, fl6->flowi6_oif, 123662306a36Sopenharmony_ci fl6->flowi6_oif != 0, skb, flags); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* Search through exception table */ 123962306a36Sopenharmony_ci rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); 124062306a36Sopenharmony_ci if (rt) { 124162306a36Sopenharmony_ci if (ip6_hold_safe(net, &rt)) 124262306a36Sopenharmony_ci dst_use_noref(&rt->dst, jiffies); 124362306a36Sopenharmony_ci } else { 124462306a36Sopenharmony_cido_create: 124562306a36Sopenharmony_ci rt = ip6_create_rt_rcu(&res); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ciout: 124962306a36Sopenharmony_ci trace_fib6_table_lookup(net, &res, table, fl6); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci rcu_read_unlock(); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return rt; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistruct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 125762306a36Sopenharmony_ci const struct sk_buff *skb, int flags) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup); 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_lookup); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistruct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 126462306a36Sopenharmony_ci const struct in6_addr *saddr, int oif, 126562306a36Sopenharmony_ci const struct sk_buff *skb, int strict) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct flowi6 fl6 = { 126862306a36Sopenharmony_ci .flowi6_oif = oif, 126962306a36Sopenharmony_ci .daddr = *daddr, 127062306a36Sopenharmony_ci }; 127162306a36Sopenharmony_ci struct dst_entry *dst; 127262306a36Sopenharmony_ci int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (saddr) { 127562306a36Sopenharmony_ci memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 127662306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup); 128062306a36Sopenharmony_ci if (dst->error == 0) 128162306a36Sopenharmony_ci return (struct rt6_info *) dst; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci dst_release(dst); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return NULL; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ciEXPORT_SYMBOL(rt6_lookup); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci/* ip6_ins_rt is called with FREE table->tb6_lock. 129062306a36Sopenharmony_ci * It takes new route entry, the addition fails by any reason the 129162306a36Sopenharmony_ci * route is released. 129262306a36Sopenharmony_ci * Caller must hold dst before calling it. 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info, 129662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci int err; 129962306a36Sopenharmony_ci struct fib6_table *table; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci table = rt->fib6_table; 130262306a36Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 130362306a36Sopenharmony_ci err = fib6_add(&table->tb6_root, rt, info, extack); 130462306a36Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return err; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ciint ip6_ins_rt(struct net *net, struct fib6_info *rt) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci struct nl_info info = { .nl_net = net, }; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci return __ip6_ins_rt(rt, &info, NULL); 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic struct rt6_info *ip6_rt_cache_alloc(const struct fib6_result *res, 131762306a36Sopenharmony_ci const struct in6_addr *daddr, 131862306a36Sopenharmony_ci const struct in6_addr *saddr) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 132162306a36Sopenharmony_ci struct net_device *dev; 132262306a36Sopenharmony_ci struct rt6_info *rt; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci * Clone the route. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 132962306a36Sopenharmony_ci return NULL; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci dev = ip6_rt_get_dev_rcu(res); 133262306a36Sopenharmony_ci rt = ip6_dst_alloc(dev_net(dev), dev, 0); 133362306a36Sopenharmony_ci if (!rt) { 133462306a36Sopenharmony_ci fib6_info_release(f6i); 133562306a36Sopenharmony_ci return NULL; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci ip6_rt_copy_init(rt, res); 133962306a36Sopenharmony_ci rt->rt6i_flags |= RTF_CACHE; 134062306a36Sopenharmony_ci rt->rt6i_dst.addr = *daddr; 134162306a36Sopenharmony_ci rt->rt6i_dst.plen = 128; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (!rt6_is_gw_or_nonexthop(res)) { 134462306a36Sopenharmony_ci if (f6i->fib6_dst.plen != 128 && 134562306a36Sopenharmony_ci ipv6_addr_equal(&f6i->fib6_dst.addr, daddr)) 134662306a36Sopenharmony_ci rt->rt6i_flags |= RTF_ANYCAST; 134762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 134862306a36Sopenharmony_ci if (rt->rt6i_src.plen && saddr) { 134962306a36Sopenharmony_ci rt->rt6i_src.addr = *saddr; 135062306a36Sopenharmony_ci rt->rt6i_src.plen = 128; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci#endif 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return rt; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 136162306a36Sopenharmony_ci unsigned short flags = fib6_info_dst_flags(f6i); 136262306a36Sopenharmony_ci struct net_device *dev; 136362306a36Sopenharmony_ci struct rt6_info *pcpu_rt; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (!fib6_info_hold_safe(f6i)) 136662306a36Sopenharmony_ci return NULL; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci rcu_read_lock(); 136962306a36Sopenharmony_ci dev = ip6_rt_get_dev_rcu(res); 137062306a36Sopenharmony_ci pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags | DST_NOCOUNT); 137162306a36Sopenharmony_ci rcu_read_unlock(); 137262306a36Sopenharmony_ci if (!pcpu_rt) { 137362306a36Sopenharmony_ci fib6_info_release(f6i); 137462306a36Sopenharmony_ci return NULL; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci ip6_rt_copy_init(pcpu_rt, res); 137762306a36Sopenharmony_ci pcpu_rt->rt6i_flags |= RTF_PCPU; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (f6i->nh) 138062306a36Sopenharmony_ci pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev)); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci return pcpu_rt; 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic bool rt6_is_valid(const struct rt6_info *rt6) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev)); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci/* It should be called with rcu_read_lock() acquired */ 139162306a36Sopenharmony_cistatic struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci struct rt6_info *pcpu_rt; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) { 139862306a36Sopenharmony_ci struct rt6_info *prev, **p; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci p = this_cpu_ptr(res->nh->rt6i_pcpu); 140162306a36Sopenharmony_ci prev = xchg(p, NULL); 140262306a36Sopenharmony_ci if (prev) { 140362306a36Sopenharmony_ci dst_dev_put(&prev->dst); 140462306a36Sopenharmony_ci dst_release(&prev->dst); 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci pcpu_rt = NULL; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return pcpu_rt; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic struct rt6_info *rt6_make_pcpu_route(struct net *net, 141462306a36Sopenharmony_ci const struct fib6_result *res) 141562306a36Sopenharmony_ci{ 141662306a36Sopenharmony_ci struct rt6_info *pcpu_rt, *prev, **p; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci pcpu_rt = ip6_rt_pcpu_alloc(res); 141962306a36Sopenharmony_ci if (!pcpu_rt) 142062306a36Sopenharmony_ci return NULL; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci p = this_cpu_ptr(res->nh->rt6i_pcpu); 142362306a36Sopenharmony_ci prev = cmpxchg(p, NULL, pcpu_rt); 142462306a36Sopenharmony_ci BUG_ON(prev); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (res->f6i->fib6_destroying) { 142762306a36Sopenharmony_ci struct fib6_info *from; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL); 143062306a36Sopenharmony_ci fib6_info_release(from); 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci return pcpu_rt; 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci/* exception hash table implementation 143762306a36Sopenharmony_ci */ 143862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(rt6_exception_lock); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci/* Remove rt6_ex from hash table and free the memory 144162306a36Sopenharmony_ci * Caller must hold rt6_exception_lock 144262306a36Sopenharmony_ci */ 144362306a36Sopenharmony_cistatic void rt6_remove_exception(struct rt6_exception_bucket *bucket, 144462306a36Sopenharmony_ci struct rt6_exception *rt6_ex) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci struct fib6_info *from; 144762306a36Sopenharmony_ci struct net *net; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci if (!bucket || !rt6_ex) 145062306a36Sopenharmony_ci return; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci net = dev_net(rt6_ex->rt6i->dst.dev); 145362306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache--; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* purge completely the exception to allow releasing the held resources: 145662306a36Sopenharmony_ci * some [sk] cache may keep the dst around for unlimited time 145762306a36Sopenharmony_ci */ 145862306a36Sopenharmony_ci from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL); 145962306a36Sopenharmony_ci fib6_info_release(from); 146062306a36Sopenharmony_ci dst_dev_put(&rt6_ex->rt6i->dst); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci hlist_del_rcu(&rt6_ex->hlist); 146362306a36Sopenharmony_ci dst_release(&rt6_ex->rt6i->dst); 146462306a36Sopenharmony_ci kfree_rcu(rt6_ex, rcu); 146562306a36Sopenharmony_ci WARN_ON_ONCE(!bucket->depth); 146662306a36Sopenharmony_ci bucket->depth--; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci/* Remove oldest rt6_ex in bucket and free the memory 147062306a36Sopenharmony_ci * Caller must hold rt6_exception_lock 147162306a36Sopenharmony_ci */ 147262306a36Sopenharmony_cistatic void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci struct rt6_exception *rt6_ex, *oldest = NULL; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (!bucket) 147762306a36Sopenharmony_ci return; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 148062306a36Sopenharmony_ci if (!oldest || time_before(rt6_ex->stamp, oldest->stamp)) 148162306a36Sopenharmony_ci oldest = rt6_ex; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci rt6_remove_exception(bucket, oldest); 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic u32 rt6_exception_hash(const struct in6_addr *dst, 148762306a36Sopenharmony_ci const struct in6_addr *src) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci static siphash_aligned_key_t rt6_exception_key; 149062306a36Sopenharmony_ci struct { 149162306a36Sopenharmony_ci struct in6_addr dst; 149262306a36Sopenharmony_ci struct in6_addr src; 149362306a36Sopenharmony_ci } __aligned(SIPHASH_ALIGNMENT) combined = { 149462306a36Sopenharmony_ci .dst = *dst, 149562306a36Sopenharmony_ci }; 149662306a36Sopenharmony_ci u64 val; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key)); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 150162306a36Sopenharmony_ci if (src) 150262306a36Sopenharmony_ci combined.src = *src; 150362306a36Sopenharmony_ci#endif 150462306a36Sopenharmony_ci val = siphash(&combined, sizeof(combined), &rt6_exception_key); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci/* Helper function to find the cached rt in the hash table 151062306a36Sopenharmony_ci * and update bucket pointer to point to the bucket for this 151162306a36Sopenharmony_ci * (daddr, saddr) pair 151262306a36Sopenharmony_ci * Caller must hold rt6_exception_lock 151362306a36Sopenharmony_ci */ 151462306a36Sopenharmony_cistatic struct rt6_exception * 151562306a36Sopenharmony_ci__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket, 151662306a36Sopenharmony_ci const struct in6_addr *daddr, 151762306a36Sopenharmony_ci const struct in6_addr *saddr) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 152062306a36Sopenharmony_ci u32 hval; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (!(*bucket) || !daddr) 152362306a36Sopenharmony_ci return NULL; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci hval = rt6_exception_hash(daddr, saddr); 152662306a36Sopenharmony_ci *bucket += hval; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) { 152962306a36Sopenharmony_ci struct rt6_info *rt6 = rt6_ex->rt6i; 153062306a36Sopenharmony_ci bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 153362306a36Sopenharmony_ci if (matched && saddr) 153462306a36Sopenharmony_ci matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 153562306a36Sopenharmony_ci#endif 153662306a36Sopenharmony_ci if (matched) 153762306a36Sopenharmony_ci return rt6_ex; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci return NULL; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci/* Helper function to find the cached rt in the hash table 154362306a36Sopenharmony_ci * and update bucket pointer to point to the bucket for this 154462306a36Sopenharmony_ci * (daddr, saddr) pair 154562306a36Sopenharmony_ci * Caller must hold rcu_read_lock() 154662306a36Sopenharmony_ci */ 154762306a36Sopenharmony_cistatic struct rt6_exception * 154862306a36Sopenharmony_ci__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, 154962306a36Sopenharmony_ci const struct in6_addr *daddr, 155062306a36Sopenharmony_ci const struct in6_addr *saddr) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 155362306a36Sopenharmony_ci u32 hval; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (!(*bucket) || !daddr) 155862306a36Sopenharmony_ci return NULL; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci hval = rt6_exception_hash(daddr, saddr); 156162306a36Sopenharmony_ci *bucket += hval; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) { 156462306a36Sopenharmony_ci struct rt6_info *rt6 = rt6_ex->rt6i; 156562306a36Sopenharmony_ci bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 156862306a36Sopenharmony_ci if (matched && saddr) 156962306a36Sopenharmony_ci matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 157062306a36Sopenharmony_ci#endif 157162306a36Sopenharmony_ci if (matched) 157262306a36Sopenharmony_ci return rt6_ex; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci return NULL; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic unsigned int fib6_mtu(const struct fib6_result *res) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci const struct fib6_nh *nh = res->nh; 158062306a36Sopenharmony_ci unsigned int mtu; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (res->f6i->fib6_pmtu) { 158362306a36Sopenharmony_ci mtu = res->f6i->fib6_pmtu; 158462306a36Sopenharmony_ci } else { 158562306a36Sopenharmony_ci struct net_device *dev = nh->fib_nh_dev; 158662306a36Sopenharmony_ci struct inet6_dev *idev; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci rcu_read_lock(); 158962306a36Sopenharmony_ci idev = __in6_dev_get(dev); 159062306a36Sopenharmony_ci mtu = idev->cnf.mtu6; 159162306a36Sopenharmony_ci rcu_read_unlock(); 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu); 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci#define FIB6_EXCEPTION_BUCKET_FLUSHED 0x1UL 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci/* used when the flushed bit is not relevant, only access to the bucket 160262306a36Sopenharmony_ci * (ie., all bucket users except rt6_insert_exception); 160362306a36Sopenharmony_ci * 160462306a36Sopenharmony_ci * called under rcu lock; sometimes called with rt6_exception_lock held 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_cistatic 160762306a36Sopenharmony_cistruct rt6_exception_bucket *fib6_nh_get_excptn_bucket(const struct fib6_nh *nh, 160862306a36Sopenharmony_ci spinlock_t *lock) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (lock) 161362306a36Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 161462306a36Sopenharmony_ci lockdep_is_held(lock)); 161562306a36Sopenharmony_ci else 161662306a36Sopenharmony_ci bucket = rcu_dereference(nh->rt6i_exception_bucket); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* remove bucket flushed bit if set */ 161962306a36Sopenharmony_ci if (bucket) { 162062306a36Sopenharmony_ci unsigned long p = (unsigned long)bucket; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci p &= ~FIB6_EXCEPTION_BUCKET_FLUSHED; 162362306a36Sopenharmony_ci bucket = (struct rt6_exception_bucket *)p; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci return bucket; 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic bool fib6_nh_excptn_bucket_flushed(struct rt6_exception_bucket *bucket) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci unsigned long p = (unsigned long)bucket; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci return !!(p & FIB6_EXCEPTION_BUCKET_FLUSHED); 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci/* called with rt6_exception_lock held */ 163762306a36Sopenharmony_cistatic void fib6_nh_excptn_bucket_set_flushed(struct fib6_nh *nh, 163862306a36Sopenharmony_ci spinlock_t *lock) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 164162306a36Sopenharmony_ci unsigned long p; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 164462306a36Sopenharmony_ci lockdep_is_held(lock)); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci p = (unsigned long)bucket; 164762306a36Sopenharmony_ci p |= FIB6_EXCEPTION_BUCKET_FLUSHED; 164862306a36Sopenharmony_ci bucket = (struct rt6_exception_bucket *)p; 164962306a36Sopenharmony_ci rcu_assign_pointer(nh->rt6i_exception_bucket, bucket); 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_cistatic int rt6_insert_exception(struct rt6_info *nrt, 165362306a36Sopenharmony_ci const struct fib6_result *res) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci struct net *net = dev_net(nrt->dst.dev); 165662306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 165762306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 165862306a36Sopenharmony_ci struct in6_addr *src_key = NULL; 165962306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 166062306a36Sopenharmony_ci struct fib6_nh *nh = res->nh; 166162306a36Sopenharmony_ci int max_depth; 166262306a36Sopenharmony_ci int err = 0; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci bucket = rcu_dereference_protected(nh->rt6i_exception_bucket, 166762306a36Sopenharmony_ci lockdep_is_held(&rt6_exception_lock)); 166862306a36Sopenharmony_ci if (!bucket) { 166962306a36Sopenharmony_ci bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket), 167062306a36Sopenharmony_ci GFP_ATOMIC); 167162306a36Sopenharmony_ci if (!bucket) { 167262306a36Sopenharmony_ci err = -ENOMEM; 167362306a36Sopenharmony_ci goto out; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci rcu_assign_pointer(nh->rt6i_exception_bucket, bucket); 167662306a36Sopenharmony_ci } else if (fib6_nh_excptn_bucket_flushed(bucket)) { 167762306a36Sopenharmony_ci err = -EINVAL; 167862306a36Sopenharmony_ci goto out; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 168262306a36Sopenharmony_ci /* fib6_src.plen != 0 indicates f6i is in subtree 168362306a36Sopenharmony_ci * and exception table is indexed by a hash of 168462306a36Sopenharmony_ci * both fib6_dst and fib6_src. 168562306a36Sopenharmony_ci * Otherwise, the exception table is indexed by 168662306a36Sopenharmony_ci * a hash of only fib6_dst. 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci if (f6i->fib6_src.plen) 168962306a36Sopenharmony_ci src_key = &nrt->rt6i_src.addr; 169062306a36Sopenharmony_ci#endif 169162306a36Sopenharmony_ci /* rt6_mtu_change() might lower mtu on f6i. 169262306a36Sopenharmony_ci * Only insert this exception route if its mtu 169362306a36Sopenharmony_ci * is less than f6i's mtu value. 169462306a36Sopenharmony_ci */ 169562306a36Sopenharmony_ci if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(res)) { 169662306a36Sopenharmony_ci err = -EINVAL; 169762306a36Sopenharmony_ci goto out; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr, 170162306a36Sopenharmony_ci src_key); 170262306a36Sopenharmony_ci if (rt6_ex) 170362306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC); 170662306a36Sopenharmony_ci if (!rt6_ex) { 170762306a36Sopenharmony_ci err = -ENOMEM; 170862306a36Sopenharmony_ci goto out; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci rt6_ex->rt6i = nrt; 171162306a36Sopenharmony_ci rt6_ex->stamp = jiffies; 171262306a36Sopenharmony_ci hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); 171362306a36Sopenharmony_ci bucket->depth++; 171462306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache++; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* Randomize max depth to avoid some side channels attacks. */ 171762306a36Sopenharmony_ci max_depth = FIB6_MAX_DEPTH + get_random_u32_below(FIB6_MAX_DEPTH); 171862306a36Sopenharmony_ci while (bucket->depth > max_depth) 171962306a36Sopenharmony_ci rt6_exception_remove_oldest(bucket); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ciout: 172262306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci /* Update fn->fn_sernum to invalidate all cached dst */ 172562306a36Sopenharmony_ci if (!err) { 172662306a36Sopenharmony_ci spin_lock_bh(&f6i->fib6_table->tb6_lock); 172762306a36Sopenharmony_ci fib6_update_sernum(net, f6i); 172862306a36Sopenharmony_ci spin_unlock_bh(&f6i->fib6_table->tb6_lock); 172962306a36Sopenharmony_ci fib6_force_start_gc(net); 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci return err; 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 173862306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 173962306a36Sopenharmony_ci struct hlist_node *tmp; 174062306a36Sopenharmony_ci int i; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 174562306a36Sopenharmony_ci if (!bucket) 174662306a36Sopenharmony_ci goto out; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci /* Prevent rt6_insert_exception() to recreate the bucket list */ 174962306a36Sopenharmony_ci if (!from) 175062306a36Sopenharmony_ci fib6_nh_excptn_bucket_set_flushed(nh, &rt6_exception_lock); 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 175362306a36Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) { 175462306a36Sopenharmony_ci if (!from || 175562306a36Sopenharmony_ci rcu_access_pointer(rt6_ex->rt6i->from) == from) 175662306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci WARN_ON_ONCE(!from && bucket->depth); 175962306a36Sopenharmony_ci bucket++; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ciout: 176262306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic int rt6_nh_flush_exceptions(struct fib6_nh *nh, void *arg) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct fib6_info *f6i = arg; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci fib6_nh_flush_exceptions(nh, f6i); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return 0; 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_civoid rt6_flush_exceptions(struct fib6_info *f6i) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci if (f6i->nh) 177762306a36Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_flush_exceptions, 177862306a36Sopenharmony_ci f6i); 177962306a36Sopenharmony_ci else 178062306a36Sopenharmony_ci fib6_nh_flush_exceptions(f6i->fib6_nh, f6i); 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci/* Find cached rt in the hash table inside passed in rt 178462306a36Sopenharmony_ci * Caller has to hold rcu_read_lock() 178562306a36Sopenharmony_ci */ 178662306a36Sopenharmony_cistatic struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 178762306a36Sopenharmony_ci const struct in6_addr *daddr, 178862306a36Sopenharmony_ci const struct in6_addr *saddr) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci const struct in6_addr *src_key = NULL; 179162306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 179262306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 179362306a36Sopenharmony_ci struct rt6_info *ret = NULL; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 179662306a36Sopenharmony_ci /* fib6i_src.plen != 0 indicates f6i is in subtree 179762306a36Sopenharmony_ci * and exception table is indexed by a hash of 179862306a36Sopenharmony_ci * both fib6_dst and fib6_src. 179962306a36Sopenharmony_ci * However, the src addr used to create the hash 180062306a36Sopenharmony_ci * might not be exactly the passed in saddr which 180162306a36Sopenharmony_ci * is a /128 addr from the flow. 180262306a36Sopenharmony_ci * So we need to use f6i->fib6_src to redo lookup 180362306a36Sopenharmony_ci * if the passed in saddr does not find anything. 180462306a36Sopenharmony_ci * (See the logic in ip6_rt_cache_alloc() on how 180562306a36Sopenharmony_ci * rt->rt6i_src is updated.) 180662306a36Sopenharmony_ci */ 180762306a36Sopenharmony_ci if (res->f6i->fib6_src.plen) 180862306a36Sopenharmony_ci src_key = saddr; 180962306a36Sopenharmony_cifind_ex: 181062306a36Sopenharmony_ci#endif 181162306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(res->nh, NULL); 181262306a36Sopenharmony_ci rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) 181562306a36Sopenharmony_ci ret = rt6_ex->rt6i; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 181862306a36Sopenharmony_ci /* Use fib6_src as src_key and redo lookup */ 181962306a36Sopenharmony_ci if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) { 182062306a36Sopenharmony_ci src_key = &res->f6i->fib6_src.addr; 182162306a36Sopenharmony_ci goto find_ex; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci#endif 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci return ret; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/* Remove the passed in cached rt from the hash table that contains it */ 182962306a36Sopenharmony_cistatic int fib6_nh_remove_exception(const struct fib6_nh *nh, int plen, 183062306a36Sopenharmony_ci const struct rt6_info *rt) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci const struct in6_addr *src_key = NULL; 183362306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 183462306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 183562306a36Sopenharmony_ci int err; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 183862306a36Sopenharmony_ci return -ENOENT; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 184162306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 184462306a36Sopenharmony_ci /* rt6i_src.plen != 0 indicates 'from' is in subtree 184562306a36Sopenharmony_ci * and exception table is indexed by a hash of 184662306a36Sopenharmony_ci * both rt6i_dst and rt6i_src. 184762306a36Sopenharmony_ci * Otherwise, the exception table is indexed by 184862306a36Sopenharmony_ci * a hash of only rt6i_dst. 184962306a36Sopenharmony_ci */ 185062306a36Sopenharmony_ci if (plen) 185162306a36Sopenharmony_ci src_key = &rt->rt6i_src.addr; 185262306a36Sopenharmony_ci#endif 185362306a36Sopenharmony_ci rt6_ex = __rt6_find_exception_spinlock(&bucket, 185462306a36Sopenharmony_ci &rt->rt6i_dst.addr, 185562306a36Sopenharmony_ci src_key); 185662306a36Sopenharmony_ci if (rt6_ex) { 185762306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 185862306a36Sopenharmony_ci err = 0; 185962306a36Sopenharmony_ci } else { 186062306a36Sopenharmony_ci err = -ENOENT; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 186462306a36Sopenharmony_ci return err; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistruct fib6_nh_excptn_arg { 186862306a36Sopenharmony_ci struct rt6_info *rt; 186962306a36Sopenharmony_ci int plen; 187062306a36Sopenharmony_ci}; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic int rt6_nh_remove_exception_rt(struct fib6_nh *nh, void *_arg) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct fib6_nh_excptn_arg *arg = _arg; 187562306a36Sopenharmony_ci int err; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci err = fib6_nh_remove_exception(nh, arg->plen, arg->rt); 187862306a36Sopenharmony_ci if (err == 0) 187962306a36Sopenharmony_ci return 1; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci return 0; 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_cistatic int rt6_remove_exception_rt(struct rt6_info *rt) 188562306a36Sopenharmony_ci{ 188662306a36Sopenharmony_ci struct fib6_info *from; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci from = rcu_dereference(rt->from); 188962306a36Sopenharmony_ci if (!from || !(rt->rt6i_flags & RTF_CACHE)) 189062306a36Sopenharmony_ci return -EINVAL; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (from->nh) { 189362306a36Sopenharmony_ci struct fib6_nh_excptn_arg arg = { 189462306a36Sopenharmony_ci .rt = rt, 189562306a36Sopenharmony_ci .plen = from->fib6_src.plen 189662306a36Sopenharmony_ci }; 189762306a36Sopenharmony_ci int rc; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci /* rc = 1 means an entry was found */ 190062306a36Sopenharmony_ci rc = nexthop_for_each_fib6_nh(from->nh, 190162306a36Sopenharmony_ci rt6_nh_remove_exception_rt, 190262306a36Sopenharmony_ci &arg); 190362306a36Sopenharmony_ci return rc ? 0 : -ENOENT; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci return fib6_nh_remove_exception(from->fib6_nh, 190762306a36Sopenharmony_ci from->fib6_src.plen, rt); 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci/* Find rt6_ex which contains the passed in rt cache and 191162306a36Sopenharmony_ci * refresh its stamp 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_cistatic void fib6_nh_update_exception(const struct fib6_nh *nh, int plen, 191462306a36Sopenharmony_ci const struct rt6_info *rt) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci const struct in6_addr *src_key = NULL; 191762306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 191862306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, NULL); 192162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 192262306a36Sopenharmony_ci /* rt6i_src.plen != 0 indicates 'from' is in subtree 192362306a36Sopenharmony_ci * and exception table is indexed by a hash of 192462306a36Sopenharmony_ci * both rt6i_dst and rt6i_src. 192562306a36Sopenharmony_ci * Otherwise, the exception table is indexed by 192662306a36Sopenharmony_ci * a hash of only rt6i_dst. 192762306a36Sopenharmony_ci */ 192862306a36Sopenharmony_ci if (plen) 192962306a36Sopenharmony_ci src_key = &rt->rt6i_src.addr; 193062306a36Sopenharmony_ci#endif 193162306a36Sopenharmony_ci rt6_ex = __rt6_find_exception_rcu(&bucket, &rt->rt6i_dst.addr, src_key); 193262306a36Sopenharmony_ci if (rt6_ex) 193362306a36Sopenharmony_ci rt6_ex->stamp = jiffies; 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cistruct fib6_nh_match_arg { 193762306a36Sopenharmony_ci const struct net_device *dev; 193862306a36Sopenharmony_ci const struct in6_addr *gw; 193962306a36Sopenharmony_ci struct fib6_nh *match; 194062306a36Sopenharmony_ci}; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci/* determine if fib6_nh has given device and gateway */ 194362306a36Sopenharmony_cistatic int fib6_nh_find_match(struct fib6_nh *nh, void *_arg) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci struct fib6_nh_match_arg *arg = _arg; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (arg->dev != nh->fib_nh_dev || 194862306a36Sopenharmony_ci (arg->gw && !nh->fib_nh_gw_family) || 194962306a36Sopenharmony_ci (!arg->gw && nh->fib_nh_gw_family) || 195062306a36Sopenharmony_ci (arg->gw && !ipv6_addr_equal(arg->gw, &nh->fib_nh_gw6))) 195162306a36Sopenharmony_ci return 0; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci arg->match = nh; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci /* found a match, break the loop */ 195662306a36Sopenharmony_ci return 1; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistatic void rt6_update_exception_stamp_rt(struct rt6_info *rt) 196062306a36Sopenharmony_ci{ 196162306a36Sopenharmony_ci struct fib6_info *from; 196262306a36Sopenharmony_ci struct fib6_nh *fib6_nh; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci rcu_read_lock(); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci from = rcu_dereference(rt->from); 196762306a36Sopenharmony_ci if (!from || !(rt->rt6i_flags & RTF_CACHE)) 196862306a36Sopenharmony_ci goto unlock; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci if (from->nh) { 197162306a36Sopenharmony_ci struct fib6_nh_match_arg arg = { 197262306a36Sopenharmony_ci .dev = rt->dst.dev, 197362306a36Sopenharmony_ci .gw = &rt->rt6i_gateway, 197462306a36Sopenharmony_ci }; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci nexthop_for_each_fib6_nh(from->nh, fib6_nh_find_match, &arg); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (!arg.match) 197962306a36Sopenharmony_ci goto unlock; 198062306a36Sopenharmony_ci fib6_nh = arg.match; 198162306a36Sopenharmony_ci } else { 198262306a36Sopenharmony_ci fib6_nh = from->fib6_nh; 198362306a36Sopenharmony_ci } 198462306a36Sopenharmony_ci fib6_nh_update_exception(fib6_nh, from->fib6_src.plen, rt); 198562306a36Sopenharmony_ciunlock: 198662306a36Sopenharmony_ci rcu_read_unlock(); 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_cistatic bool rt6_mtu_change_route_allowed(struct inet6_dev *idev, 199062306a36Sopenharmony_ci struct rt6_info *rt, int mtu) 199162306a36Sopenharmony_ci{ 199262306a36Sopenharmony_ci /* If the new MTU is lower than the route PMTU, this new MTU will be the 199362306a36Sopenharmony_ci * lowest MTU in the path: always allow updating the route PMTU to 199462306a36Sopenharmony_ci * reflect PMTU decreases. 199562306a36Sopenharmony_ci * 199662306a36Sopenharmony_ci * If the new MTU is higher, and the route PMTU is equal to the local 199762306a36Sopenharmony_ci * MTU, this means the old MTU is the lowest in the path, so allow 199862306a36Sopenharmony_ci * updating it: if other nodes now have lower MTUs, PMTU discovery will 199962306a36Sopenharmony_ci * handle this. 200062306a36Sopenharmony_ci */ 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci if (dst_mtu(&rt->dst) >= mtu) 200362306a36Sopenharmony_ci return true; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci if (dst_mtu(&rt->dst) == idev->cnf.mtu6) 200662306a36Sopenharmony_ci return true; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci return false; 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cistatic void rt6_exceptions_update_pmtu(struct inet6_dev *idev, 201262306a36Sopenharmony_ci const struct fib6_nh *nh, int mtu) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 201562306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 201662306a36Sopenharmony_ci int i; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 201962306a36Sopenharmony_ci if (!bucket) 202062306a36Sopenharmony_ci return; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 202362306a36Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 202462306a36Sopenharmony_ci struct rt6_info *entry = rt6_ex->rt6i; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected 202762306a36Sopenharmony_ci * route), the metrics of its rt->from have already 202862306a36Sopenharmony_ci * been updated. 202962306a36Sopenharmony_ci */ 203062306a36Sopenharmony_ci if (dst_metric_raw(&entry->dst, RTAX_MTU) && 203162306a36Sopenharmony_ci rt6_mtu_change_route_allowed(idev, entry, mtu)) 203262306a36Sopenharmony_ci dst_metric_set(&entry->dst, RTAX_MTU, mtu); 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci bucket++; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_cistatic void fib6_nh_exceptions_clean_tohost(const struct fib6_nh *nh, 204162306a36Sopenharmony_ci const struct in6_addr *gateway) 204262306a36Sopenharmony_ci{ 204362306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 204462306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 204562306a36Sopenharmony_ci struct hlist_node *tmp; 204662306a36Sopenharmony_ci int i; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 204962306a36Sopenharmony_ci return; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 205262306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 205362306a36Sopenharmony_ci if (bucket) { 205462306a36Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 205562306a36Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, 205662306a36Sopenharmony_ci &bucket->chain, hlist) { 205762306a36Sopenharmony_ci struct rt6_info *entry = rt6_ex->rt6i; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) == 206062306a36Sopenharmony_ci RTF_CACHE_GATEWAY && 206162306a36Sopenharmony_ci ipv6_addr_equal(gateway, 206262306a36Sopenharmony_ci &entry->rt6i_gateway)) { 206362306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci bucket++; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic void rt6_age_examine_exception(struct rt6_exception_bucket *bucket, 207462306a36Sopenharmony_ci struct rt6_exception *rt6_ex, 207562306a36Sopenharmony_ci struct fib6_gc_args *gc_args, 207662306a36Sopenharmony_ci unsigned long now) 207762306a36Sopenharmony_ci{ 207862306a36Sopenharmony_ci struct rt6_info *rt = rt6_ex->rt6i; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* we are pruning and obsoleting aged-out and non gateway exceptions 208162306a36Sopenharmony_ci * even if others have still references to them, so that on next 208262306a36Sopenharmony_ci * dst_check() such references can be dropped. 208362306a36Sopenharmony_ci * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when 208462306a36Sopenharmony_ci * expired, independently from their aging, as per RFC 8201 section 4 208562306a36Sopenharmony_ci */ 208662306a36Sopenharmony_ci if (!(rt->rt6i_flags & RTF_EXPIRES)) { 208762306a36Sopenharmony_ci if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) { 208862306a36Sopenharmony_ci RT6_TRACE("aging clone %p\n", rt); 208962306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 209062306a36Sopenharmony_ci return; 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci } else if (time_after(jiffies, rt->dst.expires)) { 209362306a36Sopenharmony_ci RT6_TRACE("purging expired route %p\n", rt); 209462306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 209562306a36Sopenharmony_ci return; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_GATEWAY) { 209962306a36Sopenharmony_ci struct neighbour *neigh; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci if (!(neigh && (neigh->flags & NTF_ROUTER))) { 210462306a36Sopenharmony_ci RT6_TRACE("purging route %p via non-router but gateway\n", 210562306a36Sopenharmony_ci rt); 210662306a36Sopenharmony_ci rt6_remove_exception(bucket, rt6_ex); 210762306a36Sopenharmony_ci return; 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci gc_args->more++; 211262306a36Sopenharmony_ci} 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_cistatic void fib6_nh_age_exceptions(const struct fib6_nh *nh, 211562306a36Sopenharmony_ci struct fib6_gc_args *gc_args, 211662306a36Sopenharmony_ci unsigned long now) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 211962306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 212062306a36Sopenharmony_ci struct hlist_node *tmp; 212162306a36Sopenharmony_ci int i; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci if (!rcu_access_pointer(nh->rt6i_exception_bucket)) 212462306a36Sopenharmony_ci return; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci rcu_read_lock_bh(); 212762306a36Sopenharmony_ci spin_lock(&rt6_exception_lock); 212862306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, &rt6_exception_lock); 212962306a36Sopenharmony_ci if (bucket) { 213062306a36Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 213162306a36Sopenharmony_ci hlist_for_each_entry_safe(rt6_ex, tmp, 213262306a36Sopenharmony_ci &bucket->chain, hlist) { 213362306a36Sopenharmony_ci rt6_age_examine_exception(bucket, rt6_ex, 213462306a36Sopenharmony_ci gc_args, now); 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci bucket++; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci spin_unlock(&rt6_exception_lock); 214062306a36Sopenharmony_ci rcu_read_unlock_bh(); 214162306a36Sopenharmony_ci} 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_cistruct fib6_nh_age_excptn_arg { 214462306a36Sopenharmony_ci struct fib6_gc_args *gc_args; 214562306a36Sopenharmony_ci unsigned long now; 214662306a36Sopenharmony_ci}; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cistatic int rt6_nh_age_exceptions(struct fib6_nh *nh, void *_arg) 214962306a36Sopenharmony_ci{ 215062306a36Sopenharmony_ci struct fib6_nh_age_excptn_arg *arg = _arg; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci fib6_nh_age_exceptions(nh, arg->gc_args, arg->now); 215362306a36Sopenharmony_ci return 0; 215462306a36Sopenharmony_ci} 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_civoid rt6_age_exceptions(struct fib6_info *f6i, 215762306a36Sopenharmony_ci struct fib6_gc_args *gc_args, 215862306a36Sopenharmony_ci unsigned long now) 215962306a36Sopenharmony_ci{ 216062306a36Sopenharmony_ci if (f6i->nh) { 216162306a36Sopenharmony_ci struct fib6_nh_age_excptn_arg arg = { 216262306a36Sopenharmony_ci .gc_args = gc_args, 216362306a36Sopenharmony_ci .now = now 216462306a36Sopenharmony_ci }; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_age_exceptions, 216762306a36Sopenharmony_ci &arg); 216862306a36Sopenharmony_ci } else { 216962306a36Sopenharmony_ci fib6_nh_age_exceptions(f6i->fib6_nh, gc_args, now); 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci} 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci/* must be called with rcu lock held */ 217462306a36Sopenharmony_ciint fib6_table_lookup(struct net *net, struct fib6_table *table, int oif, 217562306a36Sopenharmony_ci struct flowi6 *fl6, struct fib6_result *res, int strict) 217662306a36Sopenharmony_ci{ 217762306a36Sopenharmony_ci struct fib6_node *fn, *saved_fn; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 218062306a36Sopenharmony_ci saved_fn = fn; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ciredo_rt6_select: 218362306a36Sopenharmony_ci rt6_select(net, fn, oif, res, strict); 218462306a36Sopenharmony_ci if (res->f6i == net->ipv6.fib6_null_entry) { 218562306a36Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 218662306a36Sopenharmony_ci if (fn) 218762306a36Sopenharmony_ci goto redo_rt6_select; 218862306a36Sopenharmony_ci else if (strict & RT6_LOOKUP_F_REACHABLE) { 218962306a36Sopenharmony_ci /* also consider unreachable route */ 219062306a36Sopenharmony_ci strict &= ~RT6_LOOKUP_F_REACHABLE; 219162306a36Sopenharmony_ci fn = saved_fn; 219262306a36Sopenharmony_ci goto redo_rt6_select; 219362306a36Sopenharmony_ci } 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci trace_fib6_table_lookup(net, res, table, fl6); 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci return 0; 219962306a36Sopenharmony_ci} 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_cistruct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 220262306a36Sopenharmony_ci int oif, struct flowi6 *fl6, 220362306a36Sopenharmony_ci const struct sk_buff *skb, int flags) 220462306a36Sopenharmony_ci{ 220562306a36Sopenharmony_ci struct fib6_result res = {}; 220662306a36Sopenharmony_ci struct rt6_info *rt = NULL; 220762306a36Sopenharmony_ci int strict = 0; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) && 221062306a36Sopenharmony_ci !rcu_read_lock_held()); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci strict |= flags & RT6_LOOKUP_F_IFACE; 221362306a36Sopenharmony_ci strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; 221462306a36Sopenharmony_ci if (net->ipv6.devconf_all->forwarding == 0) 221562306a36Sopenharmony_ci strict |= RT6_LOOKUP_F_REACHABLE; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci rcu_read_lock(); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci fib6_table_lookup(net, table, oif, fl6, &res, strict); 222062306a36Sopenharmony_ci if (res.f6i == net->ipv6.fib6_null_entry) 222162306a36Sopenharmony_ci goto out; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci fib6_select_path(net, &res, fl6, oif, false, skb, strict); 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /*Search through exception table */ 222662306a36Sopenharmony_ci rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); 222762306a36Sopenharmony_ci if (rt) { 222862306a36Sopenharmony_ci goto out; 222962306a36Sopenharmony_ci } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 223062306a36Sopenharmony_ci !res.nh->fib_nh_gw_family)) { 223162306a36Sopenharmony_ci /* Create a RTF_CACHE clone which will not be 223262306a36Sopenharmony_ci * owned by the fib6 tree. It is for the special case where 223362306a36Sopenharmony_ci * the daddr in the skb during the neighbor look-up is different 223462306a36Sopenharmony_ci * from the fl6->daddr used to look-up route here. 223562306a36Sopenharmony_ci */ 223662306a36Sopenharmony_ci rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL); 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (rt) { 223962306a36Sopenharmony_ci /* 1 refcnt is taken during ip6_rt_cache_alloc(). 224062306a36Sopenharmony_ci * As rt6_uncached_list_add() does not consume refcnt, 224162306a36Sopenharmony_ci * this refcnt is always returned to the caller even 224262306a36Sopenharmony_ci * if caller sets RT6_LOOKUP_F_DST_NOREF flag. 224362306a36Sopenharmony_ci */ 224462306a36Sopenharmony_ci rt6_uncached_list_add(rt); 224562306a36Sopenharmony_ci rcu_read_unlock(); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci return rt; 224862306a36Sopenharmony_ci } 224962306a36Sopenharmony_ci } else { 225062306a36Sopenharmony_ci /* Get a percpu copy */ 225162306a36Sopenharmony_ci local_bh_disable(); 225262306a36Sopenharmony_ci rt = rt6_get_pcpu_route(&res); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci if (!rt) 225562306a36Sopenharmony_ci rt = rt6_make_pcpu_route(net, &res); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci local_bh_enable(); 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ciout: 226062306a36Sopenharmony_ci if (!rt) 226162306a36Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 226262306a36Sopenharmony_ci if (!(flags & RT6_LOOKUP_F_DST_NOREF)) 226362306a36Sopenharmony_ci ip6_hold_safe(net, &rt); 226462306a36Sopenharmony_ci rcu_read_unlock(); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci return rt; 226762306a36Sopenharmony_ci} 226862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_pol_route); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_input(struct net *net, 227162306a36Sopenharmony_ci struct fib6_table *table, 227262306a36Sopenharmony_ci struct flowi6 *fl6, 227362306a36Sopenharmony_ci const struct sk_buff *skb, 227462306a36Sopenharmony_ci int flags) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags); 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistruct dst_entry *ip6_route_input_lookup(struct net *net, 228062306a36Sopenharmony_ci struct net_device *dev, 228162306a36Sopenharmony_ci struct flowi6 *fl6, 228262306a36Sopenharmony_ci const struct sk_buff *skb, 228362306a36Sopenharmony_ci int flags) 228462306a36Sopenharmony_ci{ 228562306a36Sopenharmony_ci if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 228662306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_IFACE; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input); 228962306a36Sopenharmony_ci} 229062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_input_lookup); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_cistatic void ip6_multipath_l3_keys(const struct sk_buff *skb, 229362306a36Sopenharmony_ci struct flow_keys *keys, 229462306a36Sopenharmony_ci struct flow_keys *flkeys) 229562306a36Sopenharmony_ci{ 229662306a36Sopenharmony_ci const struct ipv6hdr *outer_iph = ipv6_hdr(skb); 229762306a36Sopenharmony_ci const struct ipv6hdr *key_iph = outer_iph; 229862306a36Sopenharmony_ci struct flow_keys *_flkeys = flkeys; 229962306a36Sopenharmony_ci const struct ipv6hdr *inner_iph; 230062306a36Sopenharmony_ci const struct icmp6hdr *icmph; 230162306a36Sopenharmony_ci struct ipv6hdr _inner_iph; 230262306a36Sopenharmony_ci struct icmp6hdr _icmph; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6)) 230562306a36Sopenharmony_ci goto out; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci icmph = skb_header_pointer(skb, skb_transport_offset(skb), 230862306a36Sopenharmony_ci sizeof(_icmph), &_icmph); 230962306a36Sopenharmony_ci if (!icmph) 231062306a36Sopenharmony_ci goto out; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci if (!icmpv6_is_err(icmph->icmp6_type)) 231362306a36Sopenharmony_ci goto out; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci inner_iph = skb_header_pointer(skb, 231662306a36Sopenharmony_ci skb_transport_offset(skb) + sizeof(*icmph), 231762306a36Sopenharmony_ci sizeof(_inner_iph), &_inner_iph); 231862306a36Sopenharmony_ci if (!inner_iph) 231962306a36Sopenharmony_ci goto out; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci key_iph = inner_iph; 232262306a36Sopenharmony_ci _flkeys = NULL; 232362306a36Sopenharmony_ciout: 232462306a36Sopenharmony_ci if (_flkeys) { 232562306a36Sopenharmony_ci keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src; 232662306a36Sopenharmony_ci keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst; 232762306a36Sopenharmony_ci keys->tags.flow_label = _flkeys->tags.flow_label; 232862306a36Sopenharmony_ci keys->basic.ip_proto = _flkeys->basic.ip_proto; 232962306a36Sopenharmony_ci } else { 233062306a36Sopenharmony_ci keys->addrs.v6addrs.src = key_iph->saddr; 233162306a36Sopenharmony_ci keys->addrs.v6addrs.dst = key_iph->daddr; 233262306a36Sopenharmony_ci keys->tags.flow_label = ip6_flowlabel(key_iph); 233362306a36Sopenharmony_ci keys->basic.ip_proto = key_iph->nexthdr; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci} 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_outer(const struct net *net, 233862306a36Sopenharmony_ci const struct sk_buff *skb, 233962306a36Sopenharmony_ci bool *p_has_inner) 234062306a36Sopenharmony_ci{ 234162306a36Sopenharmony_ci u32 hash_fields = ip6_multipath_hash_fields(net); 234262306a36Sopenharmony_ci struct flow_keys keys, hash_keys; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK)) 234562306a36Sopenharmony_ci return 0; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 234862306a36Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_STOP_AT_ENCAP); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 235162306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP) 235262306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src; 235362306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP) 235462306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst; 235562306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO) 235662306a36Sopenharmony_ci hash_keys.basic.ip_proto = keys.basic.ip_proto; 235762306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL) 235862306a36Sopenharmony_ci hash_keys.tags.flow_label = keys.tags.flow_label; 235962306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT) 236062306a36Sopenharmony_ci hash_keys.ports.src = keys.ports.src; 236162306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT) 236262306a36Sopenharmony_ci hash_keys.ports.dst = keys.ports.dst; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci *p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION); 236562306a36Sopenharmony_ci return flow_hash_from_keys(&hash_keys); 236662306a36Sopenharmony_ci} 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_inner(const struct net *net, 236962306a36Sopenharmony_ci const struct sk_buff *skb, 237062306a36Sopenharmony_ci bool has_inner) 237162306a36Sopenharmony_ci{ 237262306a36Sopenharmony_ci u32 hash_fields = ip6_multipath_hash_fields(net); 237362306a36Sopenharmony_ci struct flow_keys keys, hash_keys; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci /* We assume the packet carries an encapsulation, but if none was 237662306a36Sopenharmony_ci * encountered during dissection of the outer flow, then there is no 237762306a36Sopenharmony_ci * point in calling the flow dissector again. 237862306a36Sopenharmony_ci */ 237962306a36Sopenharmony_ci if (!has_inner) 238062306a36Sopenharmony_ci return 0; 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_MASK)) 238362306a36Sopenharmony_ci return 0; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 238662306a36Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, 0); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci if (!(keys.control.flags & FLOW_DIS_ENCAPSULATION)) 238962306a36Sopenharmony_ci return 0; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 239262306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 239362306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP) 239462306a36Sopenharmony_ci hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; 239562306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP) 239662306a36Sopenharmony_ci hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; 239762306a36Sopenharmony_ci } else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 239862306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 239962306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP) 240062306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src; 240162306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP) 240262306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst; 240362306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL) 240462306a36Sopenharmony_ci hash_keys.tags.flow_label = keys.tags.flow_label; 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO) 240862306a36Sopenharmony_ci hash_keys.basic.ip_proto = keys.basic.ip_proto; 240962306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_PORT) 241062306a36Sopenharmony_ci hash_keys.ports.src = keys.ports.src; 241162306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT) 241262306a36Sopenharmony_ci hash_keys.ports.dst = keys.ports.dst; 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci return flow_hash_from_keys(&hash_keys); 241562306a36Sopenharmony_ci} 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_skb(const struct net *net, 241862306a36Sopenharmony_ci const struct sk_buff *skb) 241962306a36Sopenharmony_ci{ 242062306a36Sopenharmony_ci u32 mhash, mhash_inner; 242162306a36Sopenharmony_ci bool has_inner = true; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci mhash = rt6_multipath_custom_hash_outer(net, skb, &has_inner); 242462306a36Sopenharmony_ci mhash_inner = rt6_multipath_custom_hash_inner(net, skb, has_inner); 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci return jhash_2words(mhash, mhash_inner, 0); 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_cistatic u32 rt6_multipath_custom_hash_fl6(const struct net *net, 243062306a36Sopenharmony_ci const struct flowi6 *fl6) 243162306a36Sopenharmony_ci{ 243262306a36Sopenharmony_ci u32 hash_fields = ip6_multipath_hash_fields(net); 243362306a36Sopenharmony_ci struct flow_keys hash_keys; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK)) 243662306a36Sopenharmony_ci return 0; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 243962306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 244062306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP) 244162306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 244262306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP) 244362306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 244462306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO) 244562306a36Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 244662306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL) 244762306a36Sopenharmony_ci hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); 244862306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT) 244962306a36Sopenharmony_ci hash_keys.ports.src = fl6->fl6_sport; 245062306a36Sopenharmony_ci if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT) 245162306a36Sopenharmony_ci hash_keys.ports.dst = fl6->fl6_dport; 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci return flow_hash_from_keys(&hash_keys); 245462306a36Sopenharmony_ci} 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci/* if skb is set it will be used and fl6 can be NULL */ 245762306a36Sopenharmony_ciu32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, 245862306a36Sopenharmony_ci const struct sk_buff *skb, struct flow_keys *flkeys) 245962306a36Sopenharmony_ci{ 246062306a36Sopenharmony_ci struct flow_keys hash_keys; 246162306a36Sopenharmony_ci u32 mhash = 0; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci switch (ip6_multipath_hash_policy(net)) { 246462306a36Sopenharmony_ci case 0: 246562306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 246662306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 246762306a36Sopenharmony_ci if (skb) { 246862306a36Sopenharmony_ci ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 246962306a36Sopenharmony_ci } else { 247062306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 247162306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 247262306a36Sopenharmony_ci hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); 247362306a36Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 247462306a36Sopenharmony_ci } 247562306a36Sopenharmony_ci mhash = flow_hash_from_keys(&hash_keys); 247662306a36Sopenharmony_ci break; 247762306a36Sopenharmony_ci case 1: 247862306a36Sopenharmony_ci if (skb) { 247962306a36Sopenharmony_ci unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; 248062306a36Sopenharmony_ci struct flow_keys keys; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci /* short-circuit if we already have L4 hash present */ 248362306a36Sopenharmony_ci if (skb->l4_hash) 248462306a36Sopenharmony_ci return skb_get_hash_raw(skb) >> 1; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci if (!flkeys) { 248962306a36Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, flag); 249062306a36Sopenharmony_ci flkeys = &keys; 249162306a36Sopenharmony_ci } 249262306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 249362306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 249462306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 249562306a36Sopenharmony_ci hash_keys.ports.src = flkeys->ports.src; 249662306a36Sopenharmony_ci hash_keys.ports.dst = flkeys->ports.dst; 249762306a36Sopenharmony_ci hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 249862306a36Sopenharmony_ci } else { 249962306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 250062306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 250162306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 250262306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 250362306a36Sopenharmony_ci hash_keys.ports.src = fl6->fl6_sport; 250462306a36Sopenharmony_ci hash_keys.ports.dst = fl6->fl6_dport; 250562306a36Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci mhash = flow_hash_from_keys(&hash_keys); 250862306a36Sopenharmony_ci break; 250962306a36Sopenharmony_ci case 2: 251062306a36Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 251162306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 251262306a36Sopenharmony_ci if (skb) { 251362306a36Sopenharmony_ci struct flow_keys keys; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci if (!flkeys) { 251662306a36Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, 0); 251762306a36Sopenharmony_ci flkeys = &keys; 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci /* Inner can be v4 or v6 */ 252162306a36Sopenharmony_ci if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 252262306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 252362306a36Sopenharmony_ci hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src; 252462306a36Sopenharmony_ci hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst; 252562306a36Sopenharmony_ci } else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 252662306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 252762306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 252862306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 252962306a36Sopenharmony_ci hash_keys.tags.flow_label = flkeys->tags.flow_label; 253062306a36Sopenharmony_ci hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 253162306a36Sopenharmony_ci } else { 253262306a36Sopenharmony_ci /* Same as case 0 */ 253362306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 253462306a36Sopenharmony_ci ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci } else { 253762306a36Sopenharmony_ci /* Same as case 0 */ 253862306a36Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 253962306a36Sopenharmony_ci hash_keys.addrs.v6addrs.src = fl6->saddr; 254062306a36Sopenharmony_ci hash_keys.addrs.v6addrs.dst = fl6->daddr; 254162306a36Sopenharmony_ci hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); 254262306a36Sopenharmony_ci hash_keys.basic.ip_proto = fl6->flowi6_proto; 254362306a36Sopenharmony_ci } 254462306a36Sopenharmony_ci mhash = flow_hash_from_keys(&hash_keys); 254562306a36Sopenharmony_ci break; 254662306a36Sopenharmony_ci case 3: 254762306a36Sopenharmony_ci if (skb) 254862306a36Sopenharmony_ci mhash = rt6_multipath_custom_hash_skb(net, skb); 254962306a36Sopenharmony_ci else 255062306a36Sopenharmony_ci mhash = rt6_multipath_custom_hash_fl6(net, fl6); 255162306a36Sopenharmony_ci break; 255262306a36Sopenharmony_ci } 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci return mhash >> 1; 255562306a36Sopenharmony_ci} 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci/* Called with rcu held */ 255862306a36Sopenharmony_civoid ip6_route_input(struct sk_buff *skb) 255962306a36Sopenharmony_ci{ 256062306a36Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 256162306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 256262306a36Sopenharmony_ci int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF; 256362306a36Sopenharmony_ci struct ip_tunnel_info *tun_info; 256462306a36Sopenharmony_ci struct flowi6 fl6 = { 256562306a36Sopenharmony_ci .flowi6_iif = skb->dev->ifindex, 256662306a36Sopenharmony_ci .daddr = iph->daddr, 256762306a36Sopenharmony_ci .saddr = iph->saddr, 256862306a36Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 256962306a36Sopenharmony_ci .flowi6_mark = skb->mark, 257062306a36Sopenharmony_ci .flowi6_proto = iph->nexthdr, 257162306a36Sopenharmony_ci }; 257262306a36Sopenharmony_ci struct flow_keys *flkeys = NULL, _flkeys; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci tun_info = skb_tunnel_info(skb); 257562306a36Sopenharmony_ci if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 257662306a36Sopenharmony_ci fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys)) 257962306a36Sopenharmony_ci flkeys = &_flkeys; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) 258262306a36Sopenharmony_ci fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); 258362306a36Sopenharmony_ci skb_dst_drop(skb); 258462306a36Sopenharmony_ci skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev, 258562306a36Sopenharmony_ci &fl6, skb, flags)); 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *ip6_pol_route_output(struct net *net, 258962306a36Sopenharmony_ci struct fib6_table *table, 259062306a36Sopenharmony_ci struct flowi6 *fl6, 259162306a36Sopenharmony_ci const struct sk_buff *skb, 259262306a36Sopenharmony_ci int flags) 259362306a36Sopenharmony_ci{ 259462306a36Sopenharmony_ci return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); 259562306a36Sopenharmony_ci} 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_cistatic struct dst_entry *ip6_route_output_flags_noref(struct net *net, 259862306a36Sopenharmony_ci const struct sock *sk, 259962306a36Sopenharmony_ci struct flowi6 *fl6, 260062306a36Sopenharmony_ci int flags) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci bool any_src; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci if (ipv6_addr_type(&fl6->daddr) & 260562306a36Sopenharmony_ci (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) { 260662306a36Sopenharmony_ci struct dst_entry *dst; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci /* This function does not take refcnt on the dst */ 260962306a36Sopenharmony_ci dst = l3mdev_link_scope_lookup(net, fl6); 261062306a36Sopenharmony_ci if (dst) 261162306a36Sopenharmony_ci return dst; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci fl6->flowi6_iif = LOOPBACK_IFINDEX; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_DST_NOREF; 261762306a36Sopenharmony_ci any_src = ipv6_addr_any(&fl6->saddr); 261862306a36Sopenharmony_ci if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 261962306a36Sopenharmony_ci (fl6->flowi6_oif && any_src)) 262062306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_IFACE; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci if (!any_src) 262362306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 262462306a36Sopenharmony_ci else if (sk) 262562306a36Sopenharmony_ci flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); 262862306a36Sopenharmony_ci} 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_cistruct dst_entry *ip6_route_output_flags(struct net *net, 263162306a36Sopenharmony_ci const struct sock *sk, 263262306a36Sopenharmony_ci struct flowi6 *fl6, 263362306a36Sopenharmony_ci int flags) 263462306a36Sopenharmony_ci{ 263562306a36Sopenharmony_ci struct dst_entry *dst; 263662306a36Sopenharmony_ci struct rt6_info *rt6; 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci rcu_read_lock(); 263962306a36Sopenharmony_ci dst = ip6_route_output_flags_noref(net, sk, fl6, flags); 264062306a36Sopenharmony_ci rt6 = (struct rt6_info *)dst; 264162306a36Sopenharmony_ci /* For dst cached in uncached_list, refcnt is already taken. */ 264262306a36Sopenharmony_ci if (list_empty(&rt6->dst.rt_uncached) && !dst_hold_safe(dst)) { 264362306a36Sopenharmony_ci dst = &net->ipv6.ip6_null_entry->dst; 264462306a36Sopenharmony_ci dst_hold(dst); 264562306a36Sopenharmony_ci } 264662306a36Sopenharmony_ci rcu_read_unlock(); 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci return dst; 264962306a36Sopenharmony_ci} 265062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_route_output_flags); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_cistruct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 265362306a36Sopenharmony_ci{ 265462306a36Sopenharmony_ci struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 265562306a36Sopenharmony_ci struct net_device *loopback_dev = net->loopback_dev; 265662306a36Sopenharmony_ci struct dst_entry *new = NULL; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1, 265962306a36Sopenharmony_ci DST_OBSOLETE_DEAD, 0); 266062306a36Sopenharmony_ci if (rt) { 266162306a36Sopenharmony_ci rt6_info_init(rt); 266262306a36Sopenharmony_ci atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci new = &rt->dst; 266562306a36Sopenharmony_ci new->__use = 1; 266662306a36Sopenharmony_ci new->input = dst_discard; 266762306a36Sopenharmony_ci new->output = dst_discard_out; 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci dst_copy_metrics(new, &ort->dst); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci rt->rt6i_idev = in6_dev_get(loopback_dev); 267262306a36Sopenharmony_ci rt->rt6i_gateway = ort->rt6i_gateway; 267362306a36Sopenharmony_ci rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 267662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 267762306a36Sopenharmony_ci memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 267862306a36Sopenharmony_ci#endif 267962306a36Sopenharmony_ci } 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci dst_release(dst_orig); 268262306a36Sopenharmony_ci return new ? new : ERR_PTR(-ENOMEM); 268362306a36Sopenharmony_ci} 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci/* 268662306a36Sopenharmony_ci * Destination cache support functions 268762306a36Sopenharmony_ci */ 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistatic bool fib6_check(struct fib6_info *f6i, u32 cookie) 269062306a36Sopenharmony_ci{ 269162306a36Sopenharmony_ci u32 rt_cookie = 0; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie) 269462306a36Sopenharmony_ci return false; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci if (fib6_check_expired(f6i)) 269762306a36Sopenharmony_ci return false; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci return true; 270062306a36Sopenharmony_ci} 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_cistatic struct dst_entry *rt6_check(struct rt6_info *rt, 270362306a36Sopenharmony_ci struct fib6_info *from, 270462306a36Sopenharmony_ci u32 cookie) 270562306a36Sopenharmony_ci{ 270662306a36Sopenharmony_ci u32 rt_cookie = 0; 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci if (!from || !fib6_get_cookie_safe(from, &rt_cookie) || 270962306a36Sopenharmony_ci rt_cookie != cookie) 271062306a36Sopenharmony_ci return NULL; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci if (rt6_check_expired(rt)) 271362306a36Sopenharmony_ci return NULL; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci return &rt->dst; 271662306a36Sopenharmony_ci} 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_cistatic struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, 271962306a36Sopenharmony_ci struct fib6_info *from, 272062306a36Sopenharmony_ci u32 cookie) 272162306a36Sopenharmony_ci{ 272262306a36Sopenharmony_ci if (!__rt6_check_expired(rt) && 272362306a36Sopenharmony_ci rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 272462306a36Sopenharmony_ci fib6_check(from, cookie)) 272562306a36Sopenharmony_ci return &rt->dst; 272662306a36Sopenharmony_ci else 272762306a36Sopenharmony_ci return NULL; 272862306a36Sopenharmony_ci} 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst, 273162306a36Sopenharmony_ci u32 cookie) 273262306a36Sopenharmony_ci{ 273362306a36Sopenharmony_ci struct dst_entry *dst_ret; 273462306a36Sopenharmony_ci struct fib6_info *from; 273562306a36Sopenharmony_ci struct rt6_info *rt; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci rt = container_of(dst, struct rt6_info, dst); 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci if (rt->sernum) 274062306a36Sopenharmony_ci return rt6_is_valid(rt) ? dst : NULL; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci rcu_read_lock(); 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci /* All IPV6 dsts are created with ->obsolete set to the value 274562306a36Sopenharmony_ci * DST_OBSOLETE_FORCE_CHK which forces validation calls down 274662306a36Sopenharmony_ci * into this function always. 274762306a36Sopenharmony_ci */ 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci from = rcu_dereference(rt->from); 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci if (from && (rt->rt6i_flags & RTF_PCPU || 275262306a36Sopenharmony_ci unlikely(!list_empty(&rt->dst.rt_uncached)))) 275362306a36Sopenharmony_ci dst_ret = rt6_dst_from_check(rt, from, cookie); 275462306a36Sopenharmony_ci else 275562306a36Sopenharmony_ci dst_ret = rt6_check(rt, from, cookie); 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci rcu_read_unlock(); 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci return dst_ret; 276062306a36Sopenharmony_ci} 276162306a36Sopenharmony_ciEXPORT_INDIRECT_CALLABLE(ip6_dst_check); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_cistatic struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 276462306a36Sopenharmony_ci{ 276562306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *) dst; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci if (rt) { 276862306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_CACHE) { 276962306a36Sopenharmony_ci rcu_read_lock(); 277062306a36Sopenharmony_ci if (rt6_check_expired(rt)) { 277162306a36Sopenharmony_ci rt6_remove_exception_rt(rt); 277262306a36Sopenharmony_ci dst = NULL; 277362306a36Sopenharmony_ci } 277462306a36Sopenharmony_ci rcu_read_unlock(); 277562306a36Sopenharmony_ci } else { 277662306a36Sopenharmony_ci dst_release(dst); 277762306a36Sopenharmony_ci dst = NULL; 277862306a36Sopenharmony_ci } 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci return dst; 278162306a36Sopenharmony_ci} 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_cistatic void ip6_link_failure(struct sk_buff *skb) 278462306a36Sopenharmony_ci{ 278562306a36Sopenharmony_ci struct rt6_info *rt; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci rt = (struct rt6_info *) skb_dst(skb); 279062306a36Sopenharmony_ci if (rt) { 279162306a36Sopenharmony_ci rcu_read_lock(); 279262306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_CACHE) { 279362306a36Sopenharmony_ci rt6_remove_exception_rt(rt); 279462306a36Sopenharmony_ci } else { 279562306a36Sopenharmony_ci struct fib6_info *from; 279662306a36Sopenharmony_ci struct fib6_node *fn; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci from = rcu_dereference(rt->from); 279962306a36Sopenharmony_ci if (from) { 280062306a36Sopenharmony_ci fn = rcu_dereference(from->fib6_node); 280162306a36Sopenharmony_ci if (fn && (rt->rt6i_flags & RTF_DEFAULT)) 280262306a36Sopenharmony_ci WRITE_ONCE(fn->fn_sernum, -1); 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci } 280562306a36Sopenharmony_ci rcu_read_unlock(); 280662306a36Sopenharmony_ci } 280762306a36Sopenharmony_ci} 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_cistatic void rt6_update_expires(struct rt6_info *rt0, int timeout) 281062306a36Sopenharmony_ci{ 281162306a36Sopenharmony_ci if (!(rt0->rt6i_flags & RTF_EXPIRES)) { 281262306a36Sopenharmony_ci struct fib6_info *from; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci rcu_read_lock(); 281562306a36Sopenharmony_ci from = rcu_dereference(rt0->from); 281662306a36Sopenharmony_ci if (from) 281762306a36Sopenharmony_ci rt0->dst.expires = from->expires; 281862306a36Sopenharmony_ci rcu_read_unlock(); 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci dst_set_expires(&rt0->dst, timeout); 282262306a36Sopenharmony_ci rt0->rt6i_flags |= RTF_EXPIRES; 282362306a36Sopenharmony_ci} 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_cistatic void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 282662306a36Sopenharmony_ci{ 282762306a36Sopenharmony_ci struct net *net = dev_net(rt->dst.dev); 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci dst_metric_set(&rt->dst, RTAX_MTU, mtu); 283062306a36Sopenharmony_ci rt->rt6i_flags |= RTF_MODIFIED; 283162306a36Sopenharmony_ci rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 283262306a36Sopenharmony_ci} 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_cistatic bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 283562306a36Sopenharmony_ci{ 283662306a36Sopenharmony_ci return !(rt->rt6i_flags & RTF_CACHE) && 283762306a36Sopenharmony_ci (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from)); 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_cistatic void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 284162306a36Sopenharmony_ci const struct ipv6hdr *iph, u32 mtu, 284262306a36Sopenharmony_ci bool confirm_neigh) 284362306a36Sopenharmony_ci{ 284462306a36Sopenharmony_ci const struct in6_addr *daddr, *saddr; 284562306a36Sopenharmony_ci struct rt6_info *rt6 = (struct rt6_info *)dst; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci /* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU) 284862306a36Sopenharmony_ci * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it. 284962306a36Sopenharmony_ci * [see also comment in rt6_mtu_change_route()] 285062306a36Sopenharmony_ci */ 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci if (iph) { 285362306a36Sopenharmony_ci daddr = &iph->daddr; 285462306a36Sopenharmony_ci saddr = &iph->saddr; 285562306a36Sopenharmony_ci } else if (sk) { 285662306a36Sopenharmony_ci daddr = &sk->sk_v6_daddr; 285762306a36Sopenharmony_ci saddr = &inet6_sk(sk)->saddr; 285862306a36Sopenharmony_ci } else { 285962306a36Sopenharmony_ci daddr = NULL; 286062306a36Sopenharmony_ci saddr = NULL; 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci if (confirm_neigh) 286462306a36Sopenharmony_ci dst_confirm_neigh(dst, daddr); 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci if (mtu < IPV6_MIN_MTU) 286762306a36Sopenharmony_ci return; 286862306a36Sopenharmony_ci if (mtu >= dst_mtu(dst)) 286962306a36Sopenharmony_ci return; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci if (!rt6_cache_allowed_for_pmtu(rt6)) { 287262306a36Sopenharmony_ci rt6_do_update_pmtu(rt6, mtu); 287362306a36Sopenharmony_ci /* update rt6_ex->stamp for cache */ 287462306a36Sopenharmony_ci if (rt6->rt6i_flags & RTF_CACHE) 287562306a36Sopenharmony_ci rt6_update_exception_stamp_rt(rt6); 287662306a36Sopenharmony_ci } else if (daddr) { 287762306a36Sopenharmony_ci struct fib6_result res = {}; 287862306a36Sopenharmony_ci struct rt6_info *nrt6; 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci rcu_read_lock(); 288162306a36Sopenharmony_ci res.f6i = rcu_dereference(rt6->from); 288262306a36Sopenharmony_ci if (!res.f6i) 288362306a36Sopenharmony_ci goto out_unlock; 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 288662306a36Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci if (res.f6i->nh) { 288962306a36Sopenharmony_ci struct fib6_nh_match_arg arg = { 289062306a36Sopenharmony_ci .dev = dst->dev, 289162306a36Sopenharmony_ci .gw = &rt6->rt6i_gateway, 289262306a36Sopenharmony_ci }; 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci nexthop_for_each_fib6_nh(res.f6i->nh, 289562306a36Sopenharmony_ci fib6_nh_find_match, &arg); 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci /* fib6_info uses a nexthop that does not have fib6_nh 289862306a36Sopenharmony_ci * using the dst->dev + gw. Should be impossible. 289962306a36Sopenharmony_ci */ 290062306a36Sopenharmony_ci if (!arg.match) 290162306a36Sopenharmony_ci goto out_unlock; 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci res.nh = arg.match; 290462306a36Sopenharmony_ci } else { 290562306a36Sopenharmony_ci res.nh = res.f6i->fib6_nh; 290662306a36Sopenharmony_ci } 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci nrt6 = ip6_rt_cache_alloc(&res, daddr, saddr); 290962306a36Sopenharmony_ci if (nrt6) { 291062306a36Sopenharmony_ci rt6_do_update_pmtu(nrt6, mtu); 291162306a36Sopenharmony_ci if (rt6_insert_exception(nrt6, &res)) 291262306a36Sopenharmony_ci dst_release_immediate(&nrt6->dst); 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ciout_unlock: 291562306a36Sopenharmony_ci rcu_read_unlock(); 291662306a36Sopenharmony_ci } 291762306a36Sopenharmony_ci} 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_cistatic void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 292062306a36Sopenharmony_ci struct sk_buff *skb, u32 mtu, 292162306a36Sopenharmony_ci bool confirm_neigh) 292262306a36Sopenharmony_ci{ 292362306a36Sopenharmony_ci __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu, 292462306a36Sopenharmony_ci confirm_neigh); 292562306a36Sopenharmony_ci} 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_civoid ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 292862306a36Sopenharmony_ci int oif, u32 mark, kuid_t uid) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 293162306a36Sopenharmony_ci struct dst_entry *dst; 293262306a36Sopenharmony_ci struct flowi6 fl6 = { 293362306a36Sopenharmony_ci .flowi6_oif = oif, 293462306a36Sopenharmony_ci .flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark), 293562306a36Sopenharmony_ci .daddr = iph->daddr, 293662306a36Sopenharmony_ci .saddr = iph->saddr, 293762306a36Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 293862306a36Sopenharmony_ci .flowi6_uid = uid, 293962306a36Sopenharmony_ci }; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 294262306a36Sopenharmony_ci if (!dst->error) 294362306a36Sopenharmony_ci __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true); 294462306a36Sopenharmony_ci dst_release(dst); 294562306a36Sopenharmony_ci} 294662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_update_pmtu); 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_civoid ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci int oif = sk->sk_bound_dev_if; 295162306a36Sopenharmony_ci struct dst_entry *dst; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci if (!oif && skb->dev) 295462306a36Sopenharmony_ci oif = l3mdev_master_ifindex(skb->dev); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci ip6_update_pmtu(skb, sock_net(sk), mtu, oif, READ_ONCE(sk->sk_mark), 295762306a36Sopenharmony_ci sk->sk_uid); 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci dst = __sk_dst_get(sk); 296062306a36Sopenharmony_ci if (!dst || !dst->obsolete || 296162306a36Sopenharmony_ci dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 296262306a36Sopenharmony_ci return; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci bh_lock_sock(sk); 296562306a36Sopenharmony_ci if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 296662306a36Sopenharmony_ci ip6_datagram_dst_update(sk, false); 296762306a36Sopenharmony_ci bh_unlock_sock(sk); 296862306a36Sopenharmony_ci} 296962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_civoid ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, 297262306a36Sopenharmony_ci const struct flowi6 *fl6) 297362306a36Sopenharmony_ci{ 297462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 297562306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 297662306a36Sopenharmony_ci#endif 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci ip6_dst_store(sk, dst, 297962306a36Sopenharmony_ci ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ? 298062306a36Sopenharmony_ci &sk->sk_v6_daddr : NULL, 298162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 298262306a36Sopenharmony_ci ipv6_addr_equal(&fl6->saddr, &np->saddr) ? 298362306a36Sopenharmony_ci &np->saddr : 298462306a36Sopenharmony_ci#endif 298562306a36Sopenharmony_ci NULL); 298662306a36Sopenharmony_ci} 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_cistatic bool ip6_redirect_nh_match(const struct fib6_result *res, 298962306a36Sopenharmony_ci struct flowi6 *fl6, 299062306a36Sopenharmony_ci const struct in6_addr *gw, 299162306a36Sopenharmony_ci struct rt6_info **ret) 299262306a36Sopenharmony_ci{ 299362306a36Sopenharmony_ci const struct fib6_nh *nh = res->nh; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci if (nh->fib_nh_flags & RTNH_F_DEAD || !nh->fib_nh_gw_family || 299662306a36Sopenharmony_ci fl6->flowi6_oif != nh->fib_nh_dev->ifindex) 299762306a36Sopenharmony_ci return false; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci /* rt_cache's gateway might be different from its 'parent' 300062306a36Sopenharmony_ci * in the case of an ip redirect. 300162306a36Sopenharmony_ci * So we keep searching in the exception table if the gateway 300262306a36Sopenharmony_ci * is different. 300362306a36Sopenharmony_ci */ 300462306a36Sopenharmony_ci if (!ipv6_addr_equal(gw, &nh->fib_nh_gw6)) { 300562306a36Sopenharmony_ci struct rt6_info *rt_cache; 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci rt_cache = rt6_find_cached_rt(res, &fl6->daddr, &fl6->saddr); 300862306a36Sopenharmony_ci if (rt_cache && 300962306a36Sopenharmony_ci ipv6_addr_equal(gw, &rt_cache->rt6i_gateway)) { 301062306a36Sopenharmony_ci *ret = rt_cache; 301162306a36Sopenharmony_ci return true; 301262306a36Sopenharmony_ci } 301362306a36Sopenharmony_ci return false; 301462306a36Sopenharmony_ci } 301562306a36Sopenharmony_ci return true; 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistruct fib6_nh_rd_arg { 301962306a36Sopenharmony_ci struct fib6_result *res; 302062306a36Sopenharmony_ci struct flowi6 *fl6; 302162306a36Sopenharmony_ci const struct in6_addr *gw; 302262306a36Sopenharmony_ci struct rt6_info **ret; 302362306a36Sopenharmony_ci}; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_cistatic int fib6_nh_redirect_match(struct fib6_nh *nh, void *_arg) 302662306a36Sopenharmony_ci{ 302762306a36Sopenharmony_ci struct fib6_nh_rd_arg *arg = _arg; 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci arg->res->nh = nh; 303062306a36Sopenharmony_ci return ip6_redirect_nh_match(arg->res, arg->fl6, arg->gw, arg->ret); 303162306a36Sopenharmony_ci} 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci/* Handle redirects */ 303462306a36Sopenharmony_cistruct ip6rd_flowi { 303562306a36Sopenharmony_ci struct flowi6 fl6; 303662306a36Sopenharmony_ci struct in6_addr gateway; 303762306a36Sopenharmony_ci}; 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE struct rt6_info *__ip6_route_redirect(struct net *net, 304062306a36Sopenharmony_ci struct fib6_table *table, 304162306a36Sopenharmony_ci struct flowi6 *fl6, 304262306a36Sopenharmony_ci const struct sk_buff *skb, 304362306a36Sopenharmony_ci int flags) 304462306a36Sopenharmony_ci{ 304562306a36Sopenharmony_ci struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 304662306a36Sopenharmony_ci struct rt6_info *ret = NULL; 304762306a36Sopenharmony_ci struct fib6_result res = {}; 304862306a36Sopenharmony_ci struct fib6_nh_rd_arg arg = { 304962306a36Sopenharmony_ci .res = &res, 305062306a36Sopenharmony_ci .fl6 = fl6, 305162306a36Sopenharmony_ci .gw = &rdfl->gateway, 305262306a36Sopenharmony_ci .ret = &ret 305362306a36Sopenharmony_ci }; 305462306a36Sopenharmony_ci struct fib6_info *rt; 305562306a36Sopenharmony_ci struct fib6_node *fn; 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci /* Get the "current" route for this destination and 305862306a36Sopenharmony_ci * check if the redirect has come from appropriate router. 305962306a36Sopenharmony_ci * 306062306a36Sopenharmony_ci * RFC 4861 specifies that redirects should only be 306162306a36Sopenharmony_ci * accepted if they come from the nexthop to the target. 306262306a36Sopenharmony_ci * Due to the way the routes are chosen, this notion 306362306a36Sopenharmony_ci * is a bit fuzzy and one might need to check all possible 306462306a36Sopenharmony_ci * routes. 306562306a36Sopenharmony_ci */ 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci rcu_read_lock(); 306862306a36Sopenharmony_ci fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 306962306a36Sopenharmony_cirestart: 307062306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 307162306a36Sopenharmony_ci res.f6i = rt; 307262306a36Sopenharmony_ci if (fib6_check_expired(rt)) 307362306a36Sopenharmony_ci continue; 307462306a36Sopenharmony_ci if (rt->fib6_flags & RTF_REJECT) 307562306a36Sopenharmony_ci break; 307662306a36Sopenharmony_ci if (unlikely(rt->nh)) { 307762306a36Sopenharmony_ci if (nexthop_is_blackhole(rt->nh)) 307862306a36Sopenharmony_ci continue; 307962306a36Sopenharmony_ci /* on match, res->nh is filled in and potentially ret */ 308062306a36Sopenharmony_ci if (nexthop_for_each_fib6_nh(rt->nh, 308162306a36Sopenharmony_ci fib6_nh_redirect_match, 308262306a36Sopenharmony_ci &arg)) 308362306a36Sopenharmony_ci goto out; 308462306a36Sopenharmony_ci } else { 308562306a36Sopenharmony_ci res.nh = rt->fib6_nh; 308662306a36Sopenharmony_ci if (ip6_redirect_nh_match(&res, fl6, &rdfl->gateway, 308762306a36Sopenharmony_ci &ret)) 308862306a36Sopenharmony_ci goto out; 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci } 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci if (!rt) 309362306a36Sopenharmony_ci rt = net->ipv6.fib6_null_entry; 309462306a36Sopenharmony_ci else if (rt->fib6_flags & RTF_REJECT) { 309562306a36Sopenharmony_ci ret = net->ipv6.ip6_null_entry; 309662306a36Sopenharmony_ci goto out; 309762306a36Sopenharmony_ci } 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) { 310062306a36Sopenharmony_ci fn = fib6_backtrack(fn, &fl6->saddr); 310162306a36Sopenharmony_ci if (fn) 310262306a36Sopenharmony_ci goto restart; 310362306a36Sopenharmony_ci } 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci res.f6i = rt; 310662306a36Sopenharmony_ci res.nh = rt->fib6_nh; 310762306a36Sopenharmony_ciout: 310862306a36Sopenharmony_ci if (ret) { 310962306a36Sopenharmony_ci ip6_hold_safe(net, &ret); 311062306a36Sopenharmony_ci } else { 311162306a36Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 311262306a36Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 311362306a36Sopenharmony_ci ret = ip6_create_rt_rcu(&res); 311462306a36Sopenharmony_ci } 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci rcu_read_unlock(); 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci trace_fib6_table_lookup(net, &res, table, fl6); 311962306a36Sopenharmony_ci return ret; 312062306a36Sopenharmony_ci}; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_cistatic struct dst_entry *ip6_route_redirect(struct net *net, 312362306a36Sopenharmony_ci const struct flowi6 *fl6, 312462306a36Sopenharmony_ci const struct sk_buff *skb, 312562306a36Sopenharmony_ci const struct in6_addr *gateway) 312662306a36Sopenharmony_ci{ 312762306a36Sopenharmony_ci int flags = RT6_LOOKUP_F_HAS_SADDR; 312862306a36Sopenharmony_ci struct ip6rd_flowi rdfl; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci rdfl.fl6 = *fl6; 313162306a36Sopenharmony_ci rdfl.gateway = *gateway; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci return fib6_rule_lookup(net, &rdfl.fl6, skb, 313462306a36Sopenharmony_ci flags, __ip6_route_redirect); 313562306a36Sopenharmony_ci} 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_civoid ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, 313862306a36Sopenharmony_ci kuid_t uid) 313962306a36Sopenharmony_ci{ 314062306a36Sopenharmony_ci const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 314162306a36Sopenharmony_ci struct dst_entry *dst; 314262306a36Sopenharmony_ci struct flowi6 fl6 = { 314362306a36Sopenharmony_ci .flowi6_iif = LOOPBACK_IFINDEX, 314462306a36Sopenharmony_ci .flowi6_oif = oif, 314562306a36Sopenharmony_ci .flowi6_mark = mark, 314662306a36Sopenharmony_ci .daddr = iph->daddr, 314762306a36Sopenharmony_ci .saddr = iph->saddr, 314862306a36Sopenharmony_ci .flowlabel = ip6_flowinfo(iph), 314962306a36Sopenharmony_ci .flowi6_uid = uid, 315062306a36Sopenharmony_ci }; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr); 315362306a36Sopenharmony_ci rt6_do_redirect(dst, NULL, skb); 315462306a36Sopenharmony_ci dst_release(dst); 315562306a36Sopenharmony_ci} 315662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_redirect); 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_civoid ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 316162306a36Sopenharmony_ci const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 316262306a36Sopenharmony_ci struct dst_entry *dst; 316362306a36Sopenharmony_ci struct flowi6 fl6 = { 316462306a36Sopenharmony_ci .flowi6_iif = LOOPBACK_IFINDEX, 316562306a36Sopenharmony_ci .flowi6_oif = oif, 316662306a36Sopenharmony_ci .daddr = msg->dest, 316762306a36Sopenharmony_ci .saddr = iph->daddr, 316862306a36Sopenharmony_ci .flowi6_uid = sock_net_uid(net, NULL), 316962306a36Sopenharmony_ci }; 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr); 317262306a36Sopenharmony_ci rt6_do_redirect(dst, NULL, skb); 317362306a36Sopenharmony_ci dst_release(dst); 317462306a36Sopenharmony_ci} 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_civoid ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 317762306a36Sopenharmony_ci{ 317862306a36Sopenharmony_ci ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, 317962306a36Sopenharmony_ci READ_ONCE(sk->sk_mark), sk->sk_uid); 318062306a36Sopenharmony_ci} 318162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip6_sk_redirect); 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_cistatic unsigned int ip6_default_advmss(const struct dst_entry *dst) 318462306a36Sopenharmony_ci{ 318562306a36Sopenharmony_ci struct net_device *dev = dst->dev; 318662306a36Sopenharmony_ci unsigned int mtu = dst_mtu(dst); 318762306a36Sopenharmony_ci struct net *net = dev_net(dev); 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 319262306a36Sopenharmony_ci mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci /* 319562306a36Sopenharmony_ci * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 319662306a36Sopenharmony_ci * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 319762306a36Sopenharmony_ci * IPV6_MAXPLEN is also valid and means: "any MSS, 319862306a36Sopenharmony_ci * rely only on pmtu discovery" 319962306a36Sopenharmony_ci */ 320062306a36Sopenharmony_ci if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 320162306a36Sopenharmony_ci mtu = IPV6_MAXPLEN; 320262306a36Sopenharmony_ci return mtu; 320362306a36Sopenharmony_ci} 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ciINDIRECT_CALLABLE_SCOPE unsigned int ip6_mtu(const struct dst_entry *dst) 320662306a36Sopenharmony_ci{ 320762306a36Sopenharmony_ci return ip6_dst_mtu_maybe_forward(dst, false); 320862306a36Sopenharmony_ci} 320962306a36Sopenharmony_ciEXPORT_INDIRECT_CALLABLE(ip6_mtu); 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci/* MTU selection: 321262306a36Sopenharmony_ci * 1. mtu on route is locked - use it 321362306a36Sopenharmony_ci * 2. mtu from nexthop exception 321462306a36Sopenharmony_ci * 3. mtu from egress device 321562306a36Sopenharmony_ci * 321662306a36Sopenharmony_ci * based on ip6_dst_mtu_forward and exception logic of 321762306a36Sopenharmony_ci * rt6_find_cached_rt; called with rcu_read_lock 321862306a36Sopenharmony_ci */ 321962306a36Sopenharmony_ciu32 ip6_mtu_from_fib6(const struct fib6_result *res, 322062306a36Sopenharmony_ci const struct in6_addr *daddr, 322162306a36Sopenharmony_ci const struct in6_addr *saddr) 322262306a36Sopenharmony_ci{ 322362306a36Sopenharmony_ci const struct fib6_nh *nh = res->nh; 322462306a36Sopenharmony_ci struct fib6_info *f6i = res->f6i; 322562306a36Sopenharmony_ci struct inet6_dev *idev; 322662306a36Sopenharmony_ci struct rt6_info *rt; 322762306a36Sopenharmony_ci u32 mtu = 0; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) { 323062306a36Sopenharmony_ci mtu = f6i->fib6_pmtu; 323162306a36Sopenharmony_ci if (mtu) 323262306a36Sopenharmony_ci goto out; 323362306a36Sopenharmony_ci } 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci rt = rt6_find_cached_rt(res, daddr, saddr); 323662306a36Sopenharmony_ci if (unlikely(rt)) { 323762306a36Sopenharmony_ci mtu = dst_metric_raw(&rt->dst, RTAX_MTU); 323862306a36Sopenharmony_ci } else { 323962306a36Sopenharmony_ci struct net_device *dev = nh->fib_nh_dev; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci mtu = IPV6_MIN_MTU; 324262306a36Sopenharmony_ci idev = __in6_dev_get(dev); 324362306a36Sopenharmony_ci if (idev && idev->cnf.mtu6 > mtu) 324462306a36Sopenharmony_ci mtu = idev->cnf.mtu6; 324562306a36Sopenharmony_ci } 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 324862306a36Sopenharmony_ciout: 324962306a36Sopenharmony_ci return mtu - lwtunnel_headroom(nh->fib_nh_lws, mtu); 325062306a36Sopenharmony_ci} 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_cistruct dst_entry *icmp6_dst_alloc(struct net_device *dev, 325362306a36Sopenharmony_ci struct flowi6 *fl6) 325462306a36Sopenharmony_ci{ 325562306a36Sopenharmony_ci struct dst_entry *dst; 325662306a36Sopenharmony_ci struct rt6_info *rt; 325762306a36Sopenharmony_ci struct inet6_dev *idev = in6_dev_get(dev); 325862306a36Sopenharmony_ci struct net *net = dev_net(dev); 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci if (unlikely(!idev)) 326162306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci rt = ip6_dst_alloc(net, dev, 0); 326462306a36Sopenharmony_ci if (unlikely(!rt)) { 326562306a36Sopenharmony_ci in6_dev_put(idev); 326662306a36Sopenharmony_ci dst = ERR_PTR(-ENOMEM); 326762306a36Sopenharmony_ci goto out; 326862306a36Sopenharmony_ci } 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci rt->dst.input = ip6_input; 327162306a36Sopenharmony_ci rt->dst.output = ip6_output; 327262306a36Sopenharmony_ci rt->rt6i_gateway = fl6->daddr; 327362306a36Sopenharmony_ci rt->rt6i_dst.addr = fl6->daddr; 327462306a36Sopenharmony_ci rt->rt6i_dst.plen = 128; 327562306a36Sopenharmony_ci rt->rt6i_idev = idev; 327662306a36Sopenharmony_ci dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci /* Add this dst into uncached_list so that rt6_disable_ip() can 327962306a36Sopenharmony_ci * do proper release of the net_device 328062306a36Sopenharmony_ci */ 328162306a36Sopenharmony_ci rt6_uncached_list_add(rt); 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ciout: 328662306a36Sopenharmony_ci return dst; 328762306a36Sopenharmony_ci} 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_cistatic void ip6_dst_gc(struct dst_ops *ops) 329062306a36Sopenharmony_ci{ 329162306a36Sopenharmony_ci struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 329262306a36Sopenharmony_ci int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 329362306a36Sopenharmony_ci int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 329462306a36Sopenharmony_ci int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 329562306a36Sopenharmony_ci unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 329662306a36Sopenharmony_ci unsigned int val; 329762306a36Sopenharmony_ci int entries; 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci if (time_after(rt_last_gc + rt_min_interval, jiffies)) 330062306a36Sopenharmony_ci goto out; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true); 330362306a36Sopenharmony_ci entries = dst_entries_get_slow(ops); 330462306a36Sopenharmony_ci if (entries < ops->gc_thresh) 330562306a36Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, rt_gc_timeout >> 1); 330662306a36Sopenharmony_ciout: 330762306a36Sopenharmony_ci val = atomic_read(&net->ipv6.ip6_rt_gc_expire); 330862306a36Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity)); 330962306a36Sopenharmony_ci} 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_cistatic int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg, 331262306a36Sopenharmony_ci const struct in6_addr *gw_addr, u32 tbid, 331362306a36Sopenharmony_ci int flags, struct fib6_result *res) 331462306a36Sopenharmony_ci{ 331562306a36Sopenharmony_ci struct flowi6 fl6 = { 331662306a36Sopenharmony_ci .flowi6_oif = cfg->fc_ifindex, 331762306a36Sopenharmony_ci .daddr = *gw_addr, 331862306a36Sopenharmony_ci .saddr = cfg->fc_prefsrc, 331962306a36Sopenharmony_ci }; 332062306a36Sopenharmony_ci struct fib6_table *table; 332162306a36Sopenharmony_ci int err; 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci table = fib6_get_table(net, tbid); 332462306a36Sopenharmony_ci if (!table) 332562306a36Sopenharmony_ci return -EINVAL; 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci if (!ipv6_addr_any(&cfg->fc_prefsrc)) 332862306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 332962306a36Sopenharmony_ci 333062306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci err = fib6_table_lookup(net, table, cfg->fc_ifindex, &fl6, res, flags); 333362306a36Sopenharmony_ci if (!err && res->f6i != net->ipv6.fib6_null_entry) 333462306a36Sopenharmony_ci fib6_select_path(net, res, &fl6, cfg->fc_ifindex, 333562306a36Sopenharmony_ci cfg->fc_ifindex != 0, NULL, flags); 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci return err; 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_cistatic int ip6_route_check_nh_onlink(struct net *net, 334162306a36Sopenharmony_ci struct fib6_config *cfg, 334262306a36Sopenharmony_ci const struct net_device *dev, 334362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 334462306a36Sopenharmony_ci{ 334562306a36Sopenharmony_ci u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN; 334662306a36Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 334762306a36Sopenharmony_ci struct fib6_result res = {}; 334862306a36Sopenharmony_ci int err; 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci err = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0, &res); 335162306a36Sopenharmony_ci if (!err && !(res.fib6_flags & RTF_REJECT) && 335262306a36Sopenharmony_ci /* ignore match if it is the default route */ 335362306a36Sopenharmony_ci !ipv6_addr_any(&res.f6i->fib6_dst.addr) && 335462306a36Sopenharmony_ci (res.fib6_type != RTN_UNICAST || dev != res.nh->fib_nh_dev)) { 335562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 335662306a36Sopenharmony_ci "Nexthop has invalid gateway or device mismatch"); 335762306a36Sopenharmony_ci err = -EINVAL; 335862306a36Sopenharmony_ci } 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci return err; 336162306a36Sopenharmony_ci} 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_cistatic int ip6_route_check_nh(struct net *net, 336462306a36Sopenharmony_ci struct fib6_config *cfg, 336562306a36Sopenharmony_ci struct net_device **_dev, 336662306a36Sopenharmony_ci netdevice_tracker *dev_tracker, 336762306a36Sopenharmony_ci struct inet6_dev **idev) 336862306a36Sopenharmony_ci{ 336962306a36Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 337062306a36Sopenharmony_ci struct net_device *dev = _dev ? *_dev : NULL; 337162306a36Sopenharmony_ci int flags = RT6_LOOKUP_F_IFACE; 337262306a36Sopenharmony_ci struct fib6_result res = {}; 337362306a36Sopenharmony_ci int err = -EHOSTUNREACH; 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci if (cfg->fc_table) { 337662306a36Sopenharmony_ci err = ip6_nh_lookup_table(net, cfg, gw_addr, 337762306a36Sopenharmony_ci cfg->fc_table, flags, &res); 337862306a36Sopenharmony_ci /* gw_addr can not require a gateway or resolve to a reject 337962306a36Sopenharmony_ci * route. If a device is given, it must match the result. 338062306a36Sopenharmony_ci */ 338162306a36Sopenharmony_ci if (err || res.fib6_flags & RTF_REJECT || 338262306a36Sopenharmony_ci res.nh->fib_nh_gw_family || 338362306a36Sopenharmony_ci (dev && dev != res.nh->fib_nh_dev)) 338462306a36Sopenharmony_ci err = -EHOSTUNREACH; 338562306a36Sopenharmony_ci } 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ci if (err < 0) { 338862306a36Sopenharmony_ci struct flowi6 fl6 = { 338962306a36Sopenharmony_ci .flowi6_oif = cfg->fc_ifindex, 339062306a36Sopenharmony_ci .daddr = *gw_addr, 339162306a36Sopenharmony_ci }; 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci err = fib6_lookup(net, cfg->fc_ifindex, &fl6, &res, flags); 339462306a36Sopenharmony_ci if (err || res.fib6_flags & RTF_REJECT || 339562306a36Sopenharmony_ci res.nh->fib_nh_gw_family) 339662306a36Sopenharmony_ci err = -EHOSTUNREACH; 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci if (err) 339962306a36Sopenharmony_ci return err; 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci fib6_select_path(net, &res, &fl6, cfg->fc_ifindex, 340262306a36Sopenharmony_ci cfg->fc_ifindex != 0, NULL, flags); 340362306a36Sopenharmony_ci } 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci err = 0; 340662306a36Sopenharmony_ci if (dev) { 340762306a36Sopenharmony_ci if (dev != res.nh->fib_nh_dev) 340862306a36Sopenharmony_ci err = -EHOSTUNREACH; 340962306a36Sopenharmony_ci } else { 341062306a36Sopenharmony_ci *_dev = dev = res.nh->fib_nh_dev; 341162306a36Sopenharmony_ci netdev_hold(dev, dev_tracker, GFP_ATOMIC); 341262306a36Sopenharmony_ci *idev = in6_dev_get(dev); 341362306a36Sopenharmony_ci } 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci return err; 341662306a36Sopenharmony_ci} 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cistatic int ip6_validate_gw(struct net *net, struct fib6_config *cfg, 341962306a36Sopenharmony_ci struct net_device **_dev, 342062306a36Sopenharmony_ci netdevice_tracker *dev_tracker, 342162306a36Sopenharmony_ci struct inet6_dev **idev, 342262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 342362306a36Sopenharmony_ci{ 342462306a36Sopenharmony_ci const struct in6_addr *gw_addr = &cfg->fc_gateway; 342562306a36Sopenharmony_ci int gwa_type = ipv6_addr_type(gw_addr); 342662306a36Sopenharmony_ci bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true; 342762306a36Sopenharmony_ci const struct net_device *dev = *_dev; 342862306a36Sopenharmony_ci bool need_addr_check = !dev; 342962306a36Sopenharmony_ci int err = -EINVAL; 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci /* if gw_addr is local we will fail to detect this in case 343262306a36Sopenharmony_ci * address is still TENTATIVE (DAD in progress). rt6_lookup() 343362306a36Sopenharmony_ci * will return already-added prefix route via interface that 343462306a36Sopenharmony_ci * prefix route was assigned to, which might be non-loopback. 343562306a36Sopenharmony_ci */ 343662306a36Sopenharmony_ci if (dev && 343762306a36Sopenharmony_ci ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 343862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 343962306a36Sopenharmony_ci goto out; 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) { 344362306a36Sopenharmony_ci /* IPv6 strictly inhibits using not link-local 344462306a36Sopenharmony_ci * addresses as nexthop address. 344562306a36Sopenharmony_ci * Otherwise, router will not able to send redirects. 344662306a36Sopenharmony_ci * It is very good, but in some (rare!) circumstances 344762306a36Sopenharmony_ci * (SIT, PtP, NBMA NOARP links) it is handy to allow 344862306a36Sopenharmony_ci * some exceptions. --ANK 344962306a36Sopenharmony_ci * We allow IPv4-mapped nexthops to support RFC4798-type 345062306a36Sopenharmony_ci * addressing 345162306a36Sopenharmony_ci */ 345262306a36Sopenharmony_ci if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) { 345362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid gateway address"); 345462306a36Sopenharmony_ci goto out; 345562306a36Sopenharmony_ci } 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci rcu_read_lock(); 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci if (cfg->fc_flags & RTNH_F_ONLINK) 346062306a36Sopenharmony_ci err = ip6_route_check_nh_onlink(net, cfg, dev, extack); 346162306a36Sopenharmony_ci else 346262306a36Sopenharmony_ci err = ip6_route_check_nh(net, cfg, _dev, dev_tracker, 346362306a36Sopenharmony_ci idev); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci rcu_read_unlock(); 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_ci if (err) 346862306a36Sopenharmony_ci goto out; 346962306a36Sopenharmony_ci } 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci /* reload in case device was changed */ 347262306a36Sopenharmony_ci dev = *_dev; 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci err = -EINVAL; 347562306a36Sopenharmony_ci if (!dev) { 347662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Egress device not specified"); 347762306a36Sopenharmony_ci goto out; 347862306a36Sopenharmony_ci } else if (dev->flags & IFF_LOOPBACK) { 347962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 348062306a36Sopenharmony_ci "Egress device can not be loopback device for this route"); 348162306a36Sopenharmony_ci goto out; 348262306a36Sopenharmony_ci } 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci /* if we did not check gw_addr above, do so now that the 348562306a36Sopenharmony_ci * egress device has been resolved. 348662306a36Sopenharmony_ci */ 348762306a36Sopenharmony_ci if (need_addr_check && 348862306a36Sopenharmony_ci ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 348962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 349062306a36Sopenharmony_ci goto out; 349162306a36Sopenharmony_ci } 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci err = 0; 349462306a36Sopenharmony_ciout: 349562306a36Sopenharmony_ci return err; 349662306a36Sopenharmony_ci} 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_cistatic bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci if ((flags & RTF_REJECT) || 350162306a36Sopenharmony_ci (dev && (dev->flags & IFF_LOOPBACK) && 350262306a36Sopenharmony_ci !(addr_type & IPV6_ADDR_LOOPBACK) && 350362306a36Sopenharmony_ci !(flags & (RTF_ANYCAST | RTF_LOCAL)))) 350462306a36Sopenharmony_ci return true; 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci return false; 350762306a36Sopenharmony_ci} 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ciint fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh, 351062306a36Sopenharmony_ci struct fib6_config *cfg, gfp_t gfp_flags, 351162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 351262306a36Sopenharmony_ci{ 351362306a36Sopenharmony_ci netdevice_tracker *dev_tracker = &fib6_nh->fib_nh_dev_tracker; 351462306a36Sopenharmony_ci struct net_device *dev = NULL; 351562306a36Sopenharmony_ci struct inet6_dev *idev = NULL; 351662306a36Sopenharmony_ci int addr_type; 351762306a36Sopenharmony_ci int err; 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci fib6_nh->fib_nh_family = AF_INET6; 352062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 352162306a36Sopenharmony_ci fib6_nh->last_probe = jiffies; 352262306a36Sopenharmony_ci#endif 352362306a36Sopenharmony_ci if (cfg->fc_is_fdb) { 352462306a36Sopenharmony_ci fib6_nh->fib_nh_gw6 = cfg->fc_gateway; 352562306a36Sopenharmony_ci fib6_nh->fib_nh_gw_family = AF_INET6; 352662306a36Sopenharmony_ci return 0; 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci err = -ENODEV; 353062306a36Sopenharmony_ci if (cfg->fc_ifindex) { 353162306a36Sopenharmony_ci dev = netdev_get_by_index(net, cfg->fc_ifindex, 353262306a36Sopenharmony_ci dev_tracker, gfp_flags); 353362306a36Sopenharmony_ci if (!dev) 353462306a36Sopenharmony_ci goto out; 353562306a36Sopenharmony_ci idev = in6_dev_get(dev); 353662306a36Sopenharmony_ci if (!idev) 353762306a36Sopenharmony_ci goto out; 353862306a36Sopenharmony_ci } 353962306a36Sopenharmony_ci 354062306a36Sopenharmony_ci if (cfg->fc_flags & RTNH_F_ONLINK) { 354162306a36Sopenharmony_ci if (!dev) { 354262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 354362306a36Sopenharmony_ci "Nexthop device required for onlink"); 354462306a36Sopenharmony_ci goto out; 354562306a36Sopenharmony_ci } 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci if (!(dev->flags & IFF_UP)) { 354862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 354962306a36Sopenharmony_ci err = -ENETDOWN; 355062306a36Sopenharmony_ci goto out; 355162306a36Sopenharmony_ci } 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci fib6_nh->fib_nh_flags |= RTNH_F_ONLINK; 355462306a36Sopenharmony_ci } 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci fib6_nh->fib_nh_weight = 1; 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci /* We cannot add true routes via loopback here, 355962306a36Sopenharmony_ci * they would result in kernel looping; promote them to reject routes 356062306a36Sopenharmony_ci */ 356162306a36Sopenharmony_ci addr_type = ipv6_addr_type(&cfg->fc_dst); 356262306a36Sopenharmony_ci if (fib6_is_reject(cfg->fc_flags, dev, addr_type)) { 356362306a36Sopenharmony_ci /* hold loopback dev/idev if we haven't done so. */ 356462306a36Sopenharmony_ci if (dev != net->loopback_dev) { 356562306a36Sopenharmony_ci if (dev) { 356662306a36Sopenharmony_ci netdev_put(dev, dev_tracker); 356762306a36Sopenharmony_ci in6_dev_put(idev); 356862306a36Sopenharmony_ci } 356962306a36Sopenharmony_ci dev = net->loopback_dev; 357062306a36Sopenharmony_ci netdev_hold(dev, dev_tracker, gfp_flags); 357162306a36Sopenharmony_ci idev = in6_dev_get(dev); 357262306a36Sopenharmony_ci if (!idev) { 357362306a36Sopenharmony_ci err = -ENODEV; 357462306a36Sopenharmony_ci goto out; 357562306a36Sopenharmony_ci } 357662306a36Sopenharmony_ci } 357762306a36Sopenharmony_ci goto pcpu_alloc; 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY) { 358162306a36Sopenharmony_ci err = ip6_validate_gw(net, cfg, &dev, dev_tracker, 358262306a36Sopenharmony_ci &idev, extack); 358362306a36Sopenharmony_ci if (err) 358462306a36Sopenharmony_ci goto out; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci fib6_nh->fib_nh_gw6 = cfg->fc_gateway; 358762306a36Sopenharmony_ci fib6_nh->fib_nh_gw_family = AF_INET6; 358862306a36Sopenharmony_ci } 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci err = -ENODEV; 359162306a36Sopenharmony_ci if (!dev) 359262306a36Sopenharmony_ci goto out; 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci if (idev->cnf.disable_ipv6) { 359562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device"); 359662306a36Sopenharmony_ci err = -EACCES; 359762306a36Sopenharmony_ci goto out; 359862306a36Sopenharmony_ci } 359962306a36Sopenharmony_ci 360062306a36Sopenharmony_ci if (!(dev->flags & IFF_UP) && !cfg->fc_ignore_dev_down) { 360162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 360262306a36Sopenharmony_ci err = -ENETDOWN; 360362306a36Sopenharmony_ci goto out; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci if (!(cfg->fc_flags & (RTF_LOCAL | RTF_ANYCAST)) && 360762306a36Sopenharmony_ci !netif_carrier_ok(dev)) 360862306a36Sopenharmony_ci fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap, 361162306a36Sopenharmony_ci cfg->fc_encap_type, cfg, gfp_flags, extack); 361262306a36Sopenharmony_ci if (err) 361362306a36Sopenharmony_ci goto out; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_cipcpu_alloc: 361662306a36Sopenharmony_ci fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags); 361762306a36Sopenharmony_ci if (!fib6_nh->rt6i_pcpu) { 361862306a36Sopenharmony_ci err = -ENOMEM; 361962306a36Sopenharmony_ci goto out; 362062306a36Sopenharmony_ci } 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ci fib6_nh->fib_nh_dev = dev; 362362306a36Sopenharmony_ci fib6_nh->fib_nh_oif = dev->ifindex; 362462306a36Sopenharmony_ci err = 0; 362562306a36Sopenharmony_ciout: 362662306a36Sopenharmony_ci if (idev) 362762306a36Sopenharmony_ci in6_dev_put(idev); 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci if (err) { 363062306a36Sopenharmony_ci lwtstate_put(fib6_nh->fib_nh_lws); 363162306a36Sopenharmony_ci fib6_nh->fib_nh_lws = NULL; 363262306a36Sopenharmony_ci netdev_put(dev, dev_tracker); 363362306a36Sopenharmony_ci } 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci return err; 363662306a36Sopenharmony_ci} 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_civoid fib6_nh_release(struct fib6_nh *fib6_nh) 363962306a36Sopenharmony_ci{ 364062306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci rcu_read_lock(); 364362306a36Sopenharmony_ci 364462306a36Sopenharmony_ci fib6_nh_flush_exceptions(fib6_nh, NULL); 364562306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(fib6_nh, NULL); 364662306a36Sopenharmony_ci if (bucket) { 364762306a36Sopenharmony_ci rcu_assign_pointer(fib6_nh->rt6i_exception_bucket, NULL); 364862306a36Sopenharmony_ci kfree(bucket); 364962306a36Sopenharmony_ci } 365062306a36Sopenharmony_ci 365162306a36Sopenharmony_ci rcu_read_unlock(); 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci fib6_nh_release_dsts(fib6_nh); 365462306a36Sopenharmony_ci free_percpu(fib6_nh->rt6i_pcpu); 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_ci fib_nh_common_release(&fib6_nh->nh_common); 365762306a36Sopenharmony_ci} 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_civoid fib6_nh_release_dsts(struct fib6_nh *fib6_nh) 366062306a36Sopenharmony_ci{ 366162306a36Sopenharmony_ci int cpu; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci if (!fib6_nh->rt6i_pcpu) 366462306a36Sopenharmony_ci return; 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 366762306a36Sopenharmony_ci struct rt6_info *pcpu_rt, **ppcpu_rt; 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); 367062306a36Sopenharmony_ci pcpu_rt = xchg(ppcpu_rt, NULL); 367162306a36Sopenharmony_ci if (pcpu_rt) { 367262306a36Sopenharmony_ci dst_dev_put(&pcpu_rt->dst); 367362306a36Sopenharmony_ci dst_release(&pcpu_rt->dst); 367462306a36Sopenharmony_ci } 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci} 367762306a36Sopenharmony_ci 367862306a36Sopenharmony_cistatic struct fib6_info *ip6_route_info_create(struct fib6_config *cfg, 367962306a36Sopenharmony_ci gfp_t gfp_flags, 368062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 368162306a36Sopenharmony_ci{ 368262306a36Sopenharmony_ci struct net *net = cfg->fc_nlinfo.nl_net; 368362306a36Sopenharmony_ci struct fib6_info *rt = NULL; 368462306a36Sopenharmony_ci struct nexthop *nh = NULL; 368562306a36Sopenharmony_ci struct fib6_table *table; 368662306a36Sopenharmony_ci struct fib6_nh *fib6_nh; 368762306a36Sopenharmony_ci int err = -EINVAL; 368862306a36Sopenharmony_ci int addr_type; 368962306a36Sopenharmony_ci 369062306a36Sopenharmony_ci /* RTF_PCPU is an internal flag; can not be set by userspace */ 369162306a36Sopenharmony_ci if (cfg->fc_flags & RTF_PCPU) { 369262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU"); 369362306a36Sopenharmony_ci goto out; 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci /* RTF_CACHE is an internal flag; can not be set by userspace */ 369762306a36Sopenharmony_ci if (cfg->fc_flags & RTF_CACHE) { 369862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE"); 369962306a36Sopenharmony_ci goto out; 370062306a36Sopenharmony_ci } 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci if (cfg->fc_type > RTN_MAX) { 370362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid route type"); 370462306a36Sopenharmony_ci goto out; 370562306a36Sopenharmony_ci } 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci if (cfg->fc_dst_len > 128) { 370862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid prefix length"); 370962306a36Sopenharmony_ci goto out; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci if (cfg->fc_src_len > 128) { 371262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid source address length"); 371362306a36Sopenharmony_ci goto out; 371462306a36Sopenharmony_ci } 371562306a36Sopenharmony_ci#ifndef CONFIG_IPV6_SUBTREES 371662306a36Sopenharmony_ci if (cfg->fc_src_len) { 371762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 371862306a36Sopenharmony_ci "Specifying source address requires IPV6_SUBTREES to be enabled"); 371962306a36Sopenharmony_ci goto out; 372062306a36Sopenharmony_ci } 372162306a36Sopenharmony_ci#endif 372262306a36Sopenharmony_ci if (cfg->fc_nh_id) { 372362306a36Sopenharmony_ci nh = nexthop_find_by_id(net, cfg->fc_nh_id); 372462306a36Sopenharmony_ci if (!nh) { 372562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 372662306a36Sopenharmony_ci goto out; 372762306a36Sopenharmony_ci } 372862306a36Sopenharmony_ci err = fib6_check_nexthop(nh, cfg, extack); 372962306a36Sopenharmony_ci if (err) 373062306a36Sopenharmony_ci goto out; 373162306a36Sopenharmony_ci } 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci err = -ENOBUFS; 373462306a36Sopenharmony_ci if (cfg->fc_nlinfo.nlh && 373562306a36Sopenharmony_ci !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 373662306a36Sopenharmony_ci table = fib6_get_table(net, cfg->fc_table); 373762306a36Sopenharmony_ci if (!table) { 373862306a36Sopenharmony_ci pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 373962306a36Sopenharmony_ci table = fib6_new_table(net, cfg->fc_table); 374062306a36Sopenharmony_ci } 374162306a36Sopenharmony_ci } else { 374262306a36Sopenharmony_ci table = fib6_new_table(net, cfg->fc_table); 374362306a36Sopenharmony_ci } 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci if (!table) 374662306a36Sopenharmony_ci goto out; 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci err = -ENOMEM; 374962306a36Sopenharmony_ci rt = fib6_info_alloc(gfp_flags, !nh); 375062306a36Sopenharmony_ci if (!rt) 375162306a36Sopenharmony_ci goto out; 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len, 375462306a36Sopenharmony_ci extack); 375562306a36Sopenharmony_ci if (IS_ERR(rt->fib6_metrics)) { 375662306a36Sopenharmony_ci err = PTR_ERR(rt->fib6_metrics); 375762306a36Sopenharmony_ci /* Do not leave garbage there. */ 375862306a36Sopenharmony_ci rt->fib6_metrics = (struct dst_metrics *)&dst_default_metrics; 375962306a36Sopenharmony_ci goto out_free; 376062306a36Sopenharmony_ci } 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_ci if (cfg->fc_flags & RTF_ADDRCONF) 376362306a36Sopenharmony_ci rt->dst_nocount = true; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci if (cfg->fc_flags & RTF_EXPIRES) 376662306a36Sopenharmony_ci fib6_set_expires(rt, jiffies + 376762306a36Sopenharmony_ci clock_t_to_jiffies(cfg->fc_expires)); 376862306a36Sopenharmony_ci else 376962306a36Sopenharmony_ci fib6_clean_expires(rt); 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci if (cfg->fc_protocol == RTPROT_UNSPEC) 377262306a36Sopenharmony_ci cfg->fc_protocol = RTPROT_BOOT; 377362306a36Sopenharmony_ci rt->fib6_protocol = cfg->fc_protocol; 377462306a36Sopenharmony_ci 377562306a36Sopenharmony_ci rt->fib6_table = table; 377662306a36Sopenharmony_ci rt->fib6_metric = cfg->fc_metric; 377762306a36Sopenharmony_ci rt->fib6_type = cfg->fc_type ? : RTN_UNICAST; 377862306a36Sopenharmony_ci rt->fib6_flags = cfg->fc_flags & ~RTF_GATEWAY; 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 378162306a36Sopenharmony_ci rt->fib6_dst.plen = cfg->fc_dst_len; 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 378462306a36Sopenharmony_ci ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len); 378562306a36Sopenharmony_ci rt->fib6_src.plen = cfg->fc_src_len; 378662306a36Sopenharmony_ci#endif 378762306a36Sopenharmony_ci if (nh) { 378862306a36Sopenharmony_ci if (rt->fib6_src.plen) { 378962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthops can not be used with source routing"); 379062306a36Sopenharmony_ci goto out_free; 379162306a36Sopenharmony_ci } 379262306a36Sopenharmony_ci if (!nexthop_get(nh)) { 379362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop has been deleted"); 379462306a36Sopenharmony_ci goto out_free; 379562306a36Sopenharmony_ci } 379662306a36Sopenharmony_ci rt->nh = nh; 379762306a36Sopenharmony_ci fib6_nh = nexthop_fib6_nh(rt->nh); 379862306a36Sopenharmony_ci } else { 379962306a36Sopenharmony_ci err = fib6_nh_init(net, rt->fib6_nh, cfg, gfp_flags, extack); 380062306a36Sopenharmony_ci if (err) 380162306a36Sopenharmony_ci goto out; 380262306a36Sopenharmony_ci 380362306a36Sopenharmony_ci fib6_nh = rt->fib6_nh; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci /* We cannot add true routes via loopback here, they would 380662306a36Sopenharmony_ci * result in kernel looping; promote them to reject routes 380762306a36Sopenharmony_ci */ 380862306a36Sopenharmony_ci addr_type = ipv6_addr_type(&cfg->fc_dst); 380962306a36Sopenharmony_ci if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev, 381062306a36Sopenharmony_ci addr_type)) 381162306a36Sopenharmony_ci rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP; 381262306a36Sopenharmony_ci } 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_ci if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 381562306a36Sopenharmony_ci struct net_device *dev = fib6_nh->fib_nh_dev; 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 381862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid source address"); 381962306a36Sopenharmony_ci err = -EINVAL; 382062306a36Sopenharmony_ci goto out; 382162306a36Sopenharmony_ci } 382262306a36Sopenharmony_ci rt->fib6_prefsrc.addr = cfg->fc_prefsrc; 382362306a36Sopenharmony_ci rt->fib6_prefsrc.plen = 128; 382462306a36Sopenharmony_ci } else 382562306a36Sopenharmony_ci rt->fib6_prefsrc.plen = 0; 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_ci return rt; 382862306a36Sopenharmony_ciout: 382962306a36Sopenharmony_ci fib6_info_release(rt); 383062306a36Sopenharmony_ci return ERR_PTR(err); 383162306a36Sopenharmony_ciout_free: 383262306a36Sopenharmony_ci ip_fib_metrics_put(rt->fib6_metrics); 383362306a36Sopenharmony_ci kfree(rt); 383462306a36Sopenharmony_ci return ERR_PTR(err); 383562306a36Sopenharmony_ci} 383662306a36Sopenharmony_ci 383762306a36Sopenharmony_ciint ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags, 383862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 383962306a36Sopenharmony_ci{ 384062306a36Sopenharmony_ci struct fib6_info *rt; 384162306a36Sopenharmony_ci int err; 384262306a36Sopenharmony_ci 384362306a36Sopenharmony_ci rt = ip6_route_info_create(cfg, gfp_flags, extack); 384462306a36Sopenharmony_ci if (IS_ERR(rt)) 384562306a36Sopenharmony_ci return PTR_ERR(rt); 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); 384862306a36Sopenharmony_ci fib6_info_release(rt); 384962306a36Sopenharmony_ci 385062306a36Sopenharmony_ci return err; 385162306a36Sopenharmony_ci} 385262306a36Sopenharmony_ci 385362306a36Sopenharmony_cistatic int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info) 385462306a36Sopenharmony_ci{ 385562306a36Sopenharmony_ci struct net *net = info->nl_net; 385662306a36Sopenharmony_ci struct fib6_table *table; 385762306a36Sopenharmony_ci int err; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) { 386062306a36Sopenharmony_ci err = -ENOENT; 386162306a36Sopenharmony_ci goto out; 386262306a36Sopenharmony_ci } 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci table = rt->fib6_table; 386562306a36Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 386662306a36Sopenharmony_ci err = fib6_del(rt, info); 386762306a36Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ciout: 387062306a36Sopenharmony_ci fib6_info_release(rt); 387162306a36Sopenharmony_ci return err; 387262306a36Sopenharmony_ci} 387362306a36Sopenharmony_ci 387462306a36Sopenharmony_ciint ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify) 387562306a36Sopenharmony_ci{ 387662306a36Sopenharmony_ci struct nl_info info = { 387762306a36Sopenharmony_ci .nl_net = net, 387862306a36Sopenharmony_ci .skip_notify = skip_notify 387962306a36Sopenharmony_ci }; 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci return __ip6_del_rt(rt, &info); 388262306a36Sopenharmony_ci} 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_cistatic int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) 388562306a36Sopenharmony_ci{ 388662306a36Sopenharmony_ci struct nl_info *info = &cfg->fc_nlinfo; 388762306a36Sopenharmony_ci struct net *net = info->nl_net; 388862306a36Sopenharmony_ci struct sk_buff *skb = NULL; 388962306a36Sopenharmony_ci struct fib6_table *table; 389062306a36Sopenharmony_ci int err = -ENOENT; 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) 389362306a36Sopenharmony_ci goto out_put; 389462306a36Sopenharmony_ci table = rt->fib6_table; 389562306a36Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { 389862306a36Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 389962306a36Sopenharmony_ci struct fib6_node *fn; 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_ci /* prefer to send a single notification with all hops */ 390262306a36Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 390362306a36Sopenharmony_ci if (skb) { 390462306a36Sopenharmony_ci u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci if (rt6_fill_node(net, skb, rt, NULL, 390762306a36Sopenharmony_ci NULL, NULL, 0, RTM_DELROUTE, 390862306a36Sopenharmony_ci info->portid, seq, 0) < 0) { 390962306a36Sopenharmony_ci kfree_skb(skb); 391062306a36Sopenharmony_ci skb = NULL; 391162306a36Sopenharmony_ci } else 391262306a36Sopenharmony_ci info->skip_notify = 1; 391362306a36Sopenharmony_ci } 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci /* 'rt' points to the first sibling route. If it is not the 391662306a36Sopenharmony_ci * leaf, then we do not need to send a notification. Otherwise, 391762306a36Sopenharmony_ci * we need to check if the last sibling has a next route or not 391862306a36Sopenharmony_ci * and emit a replace or delete notification, respectively. 391962306a36Sopenharmony_ci */ 392062306a36Sopenharmony_ci info->skip_notify_kernel = 1; 392162306a36Sopenharmony_ci fn = rcu_dereference_protected(rt->fib6_node, 392262306a36Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 392362306a36Sopenharmony_ci if (rcu_access_pointer(fn->leaf) == rt) { 392462306a36Sopenharmony_ci struct fib6_info *last_sibling, *replace_rt; 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci last_sibling = list_last_entry(&rt->fib6_siblings, 392762306a36Sopenharmony_ci struct fib6_info, 392862306a36Sopenharmony_ci fib6_siblings); 392962306a36Sopenharmony_ci replace_rt = rcu_dereference_protected( 393062306a36Sopenharmony_ci last_sibling->fib6_next, 393162306a36Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 393262306a36Sopenharmony_ci if (replace_rt) 393362306a36Sopenharmony_ci call_fib6_entry_notifiers_replace(net, 393462306a36Sopenharmony_ci replace_rt); 393562306a36Sopenharmony_ci else 393662306a36Sopenharmony_ci call_fib6_multipath_entry_notifiers(net, 393762306a36Sopenharmony_ci FIB_EVENT_ENTRY_DEL, 393862306a36Sopenharmony_ci rt, rt->fib6_nsiblings, 393962306a36Sopenharmony_ci NULL); 394062306a36Sopenharmony_ci } 394162306a36Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 394262306a36Sopenharmony_ci &rt->fib6_siblings, 394362306a36Sopenharmony_ci fib6_siblings) { 394462306a36Sopenharmony_ci err = fib6_del(sibling, info); 394562306a36Sopenharmony_ci if (err) 394662306a36Sopenharmony_ci goto out_unlock; 394762306a36Sopenharmony_ci } 394862306a36Sopenharmony_ci } 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_ci err = fib6_del(rt, info); 395162306a36Sopenharmony_ciout_unlock: 395262306a36Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 395362306a36Sopenharmony_ciout_put: 395462306a36Sopenharmony_ci fib6_info_release(rt); 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci if (skb) { 395762306a36Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 395862306a36Sopenharmony_ci info->nlh, gfp_any()); 395962306a36Sopenharmony_ci } 396062306a36Sopenharmony_ci return err; 396162306a36Sopenharmony_ci} 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_cistatic int __ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg) 396462306a36Sopenharmony_ci{ 396562306a36Sopenharmony_ci int rc = -ESRCH; 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex) 396862306a36Sopenharmony_ci goto out; 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY && 397162306a36Sopenharmony_ci !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 397262306a36Sopenharmony_ci goto out; 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci rc = rt6_remove_exception_rt(rt); 397562306a36Sopenharmony_ciout: 397662306a36Sopenharmony_ci return rc; 397762306a36Sopenharmony_ci} 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_cistatic int ip6_del_cached_rt(struct fib6_config *cfg, struct fib6_info *rt, 398062306a36Sopenharmony_ci struct fib6_nh *nh) 398162306a36Sopenharmony_ci{ 398262306a36Sopenharmony_ci struct fib6_result res = { 398362306a36Sopenharmony_ci .f6i = rt, 398462306a36Sopenharmony_ci .nh = nh, 398562306a36Sopenharmony_ci }; 398662306a36Sopenharmony_ci struct rt6_info *rt_cache; 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci rt_cache = rt6_find_cached_rt(&res, &cfg->fc_dst, &cfg->fc_src); 398962306a36Sopenharmony_ci if (rt_cache) 399062306a36Sopenharmony_ci return __ip6_del_cached_rt(rt_cache, cfg); 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_ci return 0; 399362306a36Sopenharmony_ci} 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_cistruct fib6_nh_del_cached_rt_arg { 399662306a36Sopenharmony_ci struct fib6_config *cfg; 399762306a36Sopenharmony_ci struct fib6_info *f6i; 399862306a36Sopenharmony_ci}; 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_cistatic int fib6_nh_del_cached_rt(struct fib6_nh *nh, void *_arg) 400162306a36Sopenharmony_ci{ 400262306a36Sopenharmony_ci struct fib6_nh_del_cached_rt_arg *arg = _arg; 400362306a36Sopenharmony_ci int rc; 400462306a36Sopenharmony_ci 400562306a36Sopenharmony_ci rc = ip6_del_cached_rt(arg->cfg, arg->f6i, nh); 400662306a36Sopenharmony_ci return rc != -ESRCH ? rc : 0; 400762306a36Sopenharmony_ci} 400862306a36Sopenharmony_ci 400962306a36Sopenharmony_cistatic int ip6_del_cached_rt_nh(struct fib6_config *cfg, struct fib6_info *f6i) 401062306a36Sopenharmony_ci{ 401162306a36Sopenharmony_ci struct fib6_nh_del_cached_rt_arg arg = { 401262306a36Sopenharmony_ci .cfg = cfg, 401362306a36Sopenharmony_ci .f6i = f6i 401462306a36Sopenharmony_ci }; 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_del_cached_rt, &arg); 401762306a36Sopenharmony_ci} 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_cistatic int ip6_route_del(struct fib6_config *cfg, 402062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 402162306a36Sopenharmony_ci{ 402262306a36Sopenharmony_ci struct fib6_table *table; 402362306a36Sopenharmony_ci struct fib6_info *rt; 402462306a36Sopenharmony_ci struct fib6_node *fn; 402562306a36Sopenharmony_ci int err = -ESRCH; 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_ci table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 402862306a36Sopenharmony_ci if (!table) { 402962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "FIB table does not exist"); 403062306a36Sopenharmony_ci return err; 403162306a36Sopenharmony_ci } 403262306a36Sopenharmony_ci 403362306a36Sopenharmony_ci rcu_read_lock(); 403462306a36Sopenharmony_ci 403562306a36Sopenharmony_ci fn = fib6_locate(&table->tb6_root, 403662306a36Sopenharmony_ci &cfg->fc_dst, cfg->fc_dst_len, 403762306a36Sopenharmony_ci &cfg->fc_src, cfg->fc_src_len, 403862306a36Sopenharmony_ci !(cfg->fc_flags & RTF_CACHE)); 403962306a36Sopenharmony_ci 404062306a36Sopenharmony_ci if (fn) { 404162306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 404262306a36Sopenharmony_ci struct fib6_nh *nh; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci if (rt->nh && cfg->fc_nh_id && 404562306a36Sopenharmony_ci rt->nh->id != cfg->fc_nh_id) 404662306a36Sopenharmony_ci continue; 404762306a36Sopenharmony_ci 404862306a36Sopenharmony_ci if (cfg->fc_flags & RTF_CACHE) { 404962306a36Sopenharmony_ci int rc = 0; 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci if (rt->nh) { 405262306a36Sopenharmony_ci rc = ip6_del_cached_rt_nh(cfg, rt); 405362306a36Sopenharmony_ci } else if (cfg->fc_nh_id) { 405462306a36Sopenharmony_ci continue; 405562306a36Sopenharmony_ci } else { 405662306a36Sopenharmony_ci nh = rt->fib6_nh; 405762306a36Sopenharmony_ci rc = ip6_del_cached_rt(cfg, rt, nh); 405862306a36Sopenharmony_ci } 405962306a36Sopenharmony_ci if (rc != -ESRCH) { 406062306a36Sopenharmony_ci rcu_read_unlock(); 406162306a36Sopenharmony_ci return rc; 406262306a36Sopenharmony_ci } 406362306a36Sopenharmony_ci continue; 406462306a36Sopenharmony_ci } 406562306a36Sopenharmony_ci 406662306a36Sopenharmony_ci if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric) 406762306a36Sopenharmony_ci continue; 406862306a36Sopenharmony_ci if (cfg->fc_protocol && 406962306a36Sopenharmony_ci cfg->fc_protocol != rt->fib6_protocol) 407062306a36Sopenharmony_ci continue; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci if (rt->nh) { 407362306a36Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 407462306a36Sopenharmony_ci continue; 407562306a36Sopenharmony_ci rcu_read_unlock(); 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_ci return __ip6_del_rt(rt, &cfg->fc_nlinfo); 407862306a36Sopenharmony_ci } 407962306a36Sopenharmony_ci if (cfg->fc_nh_id) 408062306a36Sopenharmony_ci continue; 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci nh = rt->fib6_nh; 408362306a36Sopenharmony_ci if (cfg->fc_ifindex && 408462306a36Sopenharmony_ci (!nh->fib_nh_dev || 408562306a36Sopenharmony_ci nh->fib_nh_dev->ifindex != cfg->fc_ifindex)) 408662306a36Sopenharmony_ci continue; 408762306a36Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY && 408862306a36Sopenharmony_ci !ipv6_addr_equal(&cfg->fc_gateway, &nh->fib_nh_gw6)) 408962306a36Sopenharmony_ci continue; 409062306a36Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 409162306a36Sopenharmony_ci continue; 409262306a36Sopenharmony_ci rcu_read_unlock(); 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci /* if gateway was specified only delete the one hop */ 409562306a36Sopenharmony_ci if (cfg->fc_flags & RTF_GATEWAY) 409662306a36Sopenharmony_ci return __ip6_del_rt(rt, &cfg->fc_nlinfo); 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci return __ip6_del_rt_siblings(rt, cfg); 409962306a36Sopenharmony_ci } 410062306a36Sopenharmony_ci } 410162306a36Sopenharmony_ci rcu_read_unlock(); 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci return err; 410462306a36Sopenharmony_ci} 410562306a36Sopenharmony_ci 410662306a36Sopenharmony_cistatic void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 410762306a36Sopenharmony_ci{ 410862306a36Sopenharmony_ci struct netevent_redirect netevent; 410962306a36Sopenharmony_ci struct rt6_info *rt, *nrt = NULL; 411062306a36Sopenharmony_ci struct fib6_result res = {}; 411162306a36Sopenharmony_ci struct ndisc_options ndopts; 411262306a36Sopenharmony_ci struct inet6_dev *in6_dev; 411362306a36Sopenharmony_ci struct neighbour *neigh; 411462306a36Sopenharmony_ci struct rd_msg *msg; 411562306a36Sopenharmony_ci int optlen, on_link; 411662306a36Sopenharmony_ci u8 *lladdr; 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 411962306a36Sopenharmony_ci optlen -= sizeof(*msg); 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci if (optlen < 0) { 412262306a36Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 412362306a36Sopenharmony_ci return; 412462306a36Sopenharmony_ci } 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_ci msg = (struct rd_msg *)icmp6_hdr(skb); 412762306a36Sopenharmony_ci 412862306a36Sopenharmony_ci if (ipv6_addr_is_multicast(&msg->dest)) { 412962306a36Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 413062306a36Sopenharmony_ci return; 413162306a36Sopenharmony_ci } 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ci on_link = 0; 413462306a36Sopenharmony_ci if (ipv6_addr_equal(&msg->dest, &msg->target)) { 413562306a36Sopenharmony_ci on_link = 1; 413662306a36Sopenharmony_ci } else if (ipv6_addr_type(&msg->target) != 413762306a36Sopenharmony_ci (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 413862306a36Sopenharmony_ci net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 413962306a36Sopenharmony_ci return; 414062306a36Sopenharmony_ci } 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_ci in6_dev = __in6_dev_get(skb->dev); 414362306a36Sopenharmony_ci if (!in6_dev) 414462306a36Sopenharmony_ci return; 414562306a36Sopenharmony_ci if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 414662306a36Sopenharmony_ci return; 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci /* RFC2461 8.1: 414962306a36Sopenharmony_ci * The IP source address of the Redirect MUST be the same as the current 415062306a36Sopenharmony_ci * first-hop router for the specified ICMP Destination Address. 415162306a36Sopenharmony_ci */ 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_ci if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { 415462306a36Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 415562306a36Sopenharmony_ci return; 415662306a36Sopenharmony_ci } 415762306a36Sopenharmony_ci 415862306a36Sopenharmony_ci lladdr = NULL; 415962306a36Sopenharmony_ci if (ndopts.nd_opts_tgt_lladdr) { 416062306a36Sopenharmony_ci lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 416162306a36Sopenharmony_ci skb->dev); 416262306a36Sopenharmony_ci if (!lladdr) { 416362306a36Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 416462306a36Sopenharmony_ci return; 416562306a36Sopenharmony_ci } 416662306a36Sopenharmony_ci } 416762306a36Sopenharmony_ci 416862306a36Sopenharmony_ci rt = (struct rt6_info *) dst; 416962306a36Sopenharmony_ci if (rt->rt6i_flags & RTF_REJECT) { 417062306a36Sopenharmony_ci net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 417162306a36Sopenharmony_ci return; 417262306a36Sopenharmony_ci } 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci /* Redirect received -> path was valid. 417562306a36Sopenharmony_ci * Look, redirects are sent only in response to data packets, 417662306a36Sopenharmony_ci * so that this nexthop apparently is reachable. --ANK 417762306a36Sopenharmony_ci */ 417862306a36Sopenharmony_ci dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr); 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 418162306a36Sopenharmony_ci if (!neigh) 418262306a36Sopenharmony_ci return; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci /* 418562306a36Sopenharmony_ci * We have finally decided to accept it. 418662306a36Sopenharmony_ci */ 418762306a36Sopenharmony_ci 418862306a36Sopenharmony_ci ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 418962306a36Sopenharmony_ci NEIGH_UPDATE_F_WEAK_OVERRIDE| 419062306a36Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE| 419162306a36Sopenharmony_ci (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 419262306a36Sopenharmony_ci NEIGH_UPDATE_F_ISROUTER)), 419362306a36Sopenharmony_ci NDISC_REDIRECT, &ndopts); 419462306a36Sopenharmony_ci 419562306a36Sopenharmony_ci rcu_read_lock(); 419662306a36Sopenharmony_ci res.f6i = rcu_dereference(rt->from); 419762306a36Sopenharmony_ci if (!res.f6i) 419862306a36Sopenharmony_ci goto out; 419962306a36Sopenharmony_ci 420062306a36Sopenharmony_ci if (res.f6i->nh) { 420162306a36Sopenharmony_ci struct fib6_nh_match_arg arg = { 420262306a36Sopenharmony_ci .dev = dst->dev, 420362306a36Sopenharmony_ci .gw = &rt->rt6i_gateway, 420462306a36Sopenharmony_ci }; 420562306a36Sopenharmony_ci 420662306a36Sopenharmony_ci nexthop_for_each_fib6_nh(res.f6i->nh, 420762306a36Sopenharmony_ci fib6_nh_find_match, &arg); 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci /* fib6_info uses a nexthop that does not have fib6_nh 421062306a36Sopenharmony_ci * using the dst->dev. Should be impossible 421162306a36Sopenharmony_ci */ 421262306a36Sopenharmony_ci if (!arg.match) 421362306a36Sopenharmony_ci goto out; 421462306a36Sopenharmony_ci res.nh = arg.match; 421562306a36Sopenharmony_ci } else { 421662306a36Sopenharmony_ci res.nh = res.f6i->fib6_nh; 421762306a36Sopenharmony_ci } 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci res.fib6_flags = res.f6i->fib6_flags; 422062306a36Sopenharmony_ci res.fib6_type = res.f6i->fib6_type; 422162306a36Sopenharmony_ci nrt = ip6_rt_cache_alloc(&res, &msg->dest, NULL); 422262306a36Sopenharmony_ci if (!nrt) 422362306a36Sopenharmony_ci goto out; 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 422662306a36Sopenharmony_ci if (on_link) 422762306a36Sopenharmony_ci nrt->rt6i_flags &= ~RTF_GATEWAY; 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci /* rt6_insert_exception() will take care of duplicated exceptions */ 423262306a36Sopenharmony_ci if (rt6_insert_exception(nrt, &res)) { 423362306a36Sopenharmony_ci dst_release_immediate(&nrt->dst); 423462306a36Sopenharmony_ci goto out; 423562306a36Sopenharmony_ci } 423662306a36Sopenharmony_ci 423762306a36Sopenharmony_ci netevent.old = &rt->dst; 423862306a36Sopenharmony_ci netevent.new = &nrt->dst; 423962306a36Sopenharmony_ci netevent.daddr = &msg->dest; 424062306a36Sopenharmony_ci netevent.neigh = neigh; 424162306a36Sopenharmony_ci call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ciout: 424462306a36Sopenharmony_ci rcu_read_unlock(); 424562306a36Sopenharmony_ci neigh_release(neigh); 424662306a36Sopenharmony_ci} 424762306a36Sopenharmony_ci 424862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 424962306a36Sopenharmony_cistatic struct fib6_info *rt6_get_route_info(struct net *net, 425062306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 425162306a36Sopenharmony_ci const struct in6_addr *gwaddr, 425262306a36Sopenharmony_ci struct net_device *dev) 425362306a36Sopenharmony_ci{ 425462306a36Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 425562306a36Sopenharmony_ci int ifindex = dev->ifindex; 425662306a36Sopenharmony_ci struct fib6_node *fn; 425762306a36Sopenharmony_ci struct fib6_info *rt = NULL; 425862306a36Sopenharmony_ci struct fib6_table *table; 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci table = fib6_get_table(net, tb_id); 426162306a36Sopenharmony_ci if (!table) 426262306a36Sopenharmony_ci return NULL; 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_ci rcu_read_lock(); 426562306a36Sopenharmony_ci fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); 426662306a36Sopenharmony_ci if (!fn) 426762306a36Sopenharmony_ci goto out; 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 427062306a36Sopenharmony_ci /* these routes do not use nexthops */ 427162306a36Sopenharmony_ci if (rt->nh) 427262306a36Sopenharmony_ci continue; 427362306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev->ifindex != ifindex) 427462306a36Sopenharmony_ci continue; 427562306a36Sopenharmony_ci if (!(rt->fib6_flags & RTF_ROUTEINFO) || 427662306a36Sopenharmony_ci !rt->fib6_nh->fib_nh_gw_family) 427762306a36Sopenharmony_ci continue; 427862306a36Sopenharmony_ci if (!ipv6_addr_equal(&rt->fib6_nh->fib_nh_gw6, gwaddr)) 427962306a36Sopenharmony_ci continue; 428062306a36Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 428162306a36Sopenharmony_ci continue; 428262306a36Sopenharmony_ci break; 428362306a36Sopenharmony_ci } 428462306a36Sopenharmony_ciout: 428562306a36Sopenharmony_ci rcu_read_unlock(); 428662306a36Sopenharmony_ci return rt; 428762306a36Sopenharmony_ci} 428862306a36Sopenharmony_ci 428962306a36Sopenharmony_cistatic struct fib6_info *rt6_add_route_info(struct net *net, 429062306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 429162306a36Sopenharmony_ci const struct in6_addr *gwaddr, 429262306a36Sopenharmony_ci struct net_device *dev, 429362306a36Sopenharmony_ci unsigned int pref) 429462306a36Sopenharmony_ci{ 429562306a36Sopenharmony_ci struct fib6_config cfg = { 429662306a36Sopenharmony_ci .fc_metric = IP6_RT_PRIO_USER, 429762306a36Sopenharmony_ci .fc_ifindex = dev->ifindex, 429862306a36Sopenharmony_ci .fc_dst_len = prefixlen, 429962306a36Sopenharmony_ci .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 430062306a36Sopenharmony_ci RTF_UP | RTF_PREF(pref), 430162306a36Sopenharmony_ci .fc_protocol = RTPROT_RA, 430262306a36Sopenharmony_ci .fc_type = RTN_UNICAST, 430362306a36Sopenharmony_ci .fc_nlinfo.portid = 0, 430462306a36Sopenharmony_ci .fc_nlinfo.nlh = NULL, 430562306a36Sopenharmony_ci .fc_nlinfo.nl_net = net, 430662306a36Sopenharmony_ci }; 430762306a36Sopenharmony_ci 430862306a36Sopenharmony_ci cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 430962306a36Sopenharmony_ci cfg.fc_dst = *prefix; 431062306a36Sopenharmony_ci cfg.fc_gateway = *gwaddr; 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_ci /* We should treat it as a default route if prefix length is 0. */ 431362306a36Sopenharmony_ci if (!prefixlen) 431462306a36Sopenharmony_ci cfg.fc_flags |= RTF_DEFAULT; 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_ci ip6_route_add(&cfg, GFP_ATOMIC, NULL); 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_ci return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); 431962306a36Sopenharmony_ci} 432062306a36Sopenharmony_ci#endif 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_cistruct fib6_info *rt6_get_dflt_router(struct net *net, 432362306a36Sopenharmony_ci const struct in6_addr *addr, 432462306a36Sopenharmony_ci struct net_device *dev) 432562306a36Sopenharmony_ci{ 432662306a36Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; 432762306a36Sopenharmony_ci struct fib6_info *rt; 432862306a36Sopenharmony_ci struct fib6_table *table; 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci table = fib6_get_table(net, tb_id); 433162306a36Sopenharmony_ci if (!table) 433262306a36Sopenharmony_ci return NULL; 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci rcu_read_lock(); 433562306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(&table->tb6_root) { 433662306a36Sopenharmony_ci struct fib6_nh *nh; 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_ci /* RA routes do not use nexthops */ 433962306a36Sopenharmony_ci if (rt->nh) 434062306a36Sopenharmony_ci continue; 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci nh = rt->fib6_nh; 434362306a36Sopenharmony_ci if (dev == nh->fib_nh_dev && 434462306a36Sopenharmony_ci ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 434562306a36Sopenharmony_ci ipv6_addr_equal(&nh->fib_nh_gw6, addr)) 434662306a36Sopenharmony_ci break; 434762306a36Sopenharmony_ci } 434862306a36Sopenharmony_ci if (rt && !fib6_info_hold_safe(rt)) 434962306a36Sopenharmony_ci rt = NULL; 435062306a36Sopenharmony_ci rcu_read_unlock(); 435162306a36Sopenharmony_ci return rt; 435262306a36Sopenharmony_ci} 435362306a36Sopenharmony_ci 435462306a36Sopenharmony_cistruct fib6_info *rt6_add_dflt_router(struct net *net, 435562306a36Sopenharmony_ci const struct in6_addr *gwaddr, 435662306a36Sopenharmony_ci struct net_device *dev, 435762306a36Sopenharmony_ci unsigned int pref, 435862306a36Sopenharmony_ci u32 defrtr_usr_metric) 435962306a36Sopenharmony_ci{ 436062306a36Sopenharmony_ci struct fib6_config cfg = { 436162306a36Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 436262306a36Sopenharmony_ci .fc_metric = defrtr_usr_metric, 436362306a36Sopenharmony_ci .fc_ifindex = dev->ifindex, 436462306a36Sopenharmony_ci .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 436562306a36Sopenharmony_ci RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 436662306a36Sopenharmony_ci .fc_protocol = RTPROT_RA, 436762306a36Sopenharmony_ci .fc_type = RTN_UNICAST, 436862306a36Sopenharmony_ci .fc_nlinfo.portid = 0, 436962306a36Sopenharmony_ci .fc_nlinfo.nlh = NULL, 437062306a36Sopenharmony_ci .fc_nlinfo.nl_net = net, 437162306a36Sopenharmony_ci }; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci cfg.fc_gateway = *gwaddr; 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) { 437662306a36Sopenharmony_ci struct fib6_table *table; 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci table = fib6_get_table(dev_net(dev), cfg.fc_table); 437962306a36Sopenharmony_ci if (table) 438062306a36Sopenharmony_ci table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; 438162306a36Sopenharmony_ci } 438262306a36Sopenharmony_ci 438362306a36Sopenharmony_ci return rt6_get_dflt_router(net, gwaddr, dev); 438462306a36Sopenharmony_ci} 438562306a36Sopenharmony_ci 438662306a36Sopenharmony_cistatic void __rt6_purge_dflt_routers(struct net *net, 438762306a36Sopenharmony_ci struct fib6_table *table) 438862306a36Sopenharmony_ci{ 438962306a36Sopenharmony_ci struct fib6_info *rt; 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_cirestart: 439262306a36Sopenharmony_ci rcu_read_lock(); 439362306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(&table->tb6_root) { 439462306a36Sopenharmony_ci struct net_device *dev = fib6_info_nh_dev(rt); 439562306a36Sopenharmony_ci struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; 439662306a36Sopenharmony_ci 439762306a36Sopenharmony_ci if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 439862306a36Sopenharmony_ci (!idev || idev->cnf.accept_ra != 2) && 439962306a36Sopenharmony_ci fib6_info_hold_safe(rt)) { 440062306a36Sopenharmony_ci rcu_read_unlock(); 440162306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 440262306a36Sopenharmony_ci goto restart; 440362306a36Sopenharmony_ci } 440462306a36Sopenharmony_ci } 440562306a36Sopenharmony_ci rcu_read_unlock(); 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; 440862306a36Sopenharmony_ci} 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_civoid rt6_purge_dflt_routers(struct net *net) 441162306a36Sopenharmony_ci{ 441262306a36Sopenharmony_ci struct fib6_table *table; 441362306a36Sopenharmony_ci struct hlist_head *head; 441462306a36Sopenharmony_ci unsigned int h; 441562306a36Sopenharmony_ci 441662306a36Sopenharmony_ci rcu_read_lock(); 441762306a36Sopenharmony_ci 441862306a36Sopenharmony_ci for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 441962306a36Sopenharmony_ci head = &net->ipv6.fib_table_hash[h]; 442062306a36Sopenharmony_ci hlist_for_each_entry_rcu(table, head, tb6_hlist) { 442162306a36Sopenharmony_ci if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) 442262306a36Sopenharmony_ci __rt6_purge_dflt_routers(net, table); 442362306a36Sopenharmony_ci } 442462306a36Sopenharmony_ci } 442562306a36Sopenharmony_ci 442662306a36Sopenharmony_ci rcu_read_unlock(); 442762306a36Sopenharmony_ci} 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_cistatic void rtmsg_to_fib6_config(struct net *net, 443062306a36Sopenharmony_ci struct in6_rtmsg *rtmsg, 443162306a36Sopenharmony_ci struct fib6_config *cfg) 443262306a36Sopenharmony_ci{ 443362306a36Sopenharmony_ci *cfg = (struct fib6_config){ 443462306a36Sopenharmony_ci .fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 443562306a36Sopenharmony_ci : RT6_TABLE_MAIN, 443662306a36Sopenharmony_ci .fc_ifindex = rtmsg->rtmsg_ifindex, 443762306a36Sopenharmony_ci .fc_metric = rtmsg->rtmsg_metric ? : IP6_RT_PRIO_USER, 443862306a36Sopenharmony_ci .fc_expires = rtmsg->rtmsg_info, 443962306a36Sopenharmony_ci .fc_dst_len = rtmsg->rtmsg_dst_len, 444062306a36Sopenharmony_ci .fc_src_len = rtmsg->rtmsg_src_len, 444162306a36Sopenharmony_ci .fc_flags = rtmsg->rtmsg_flags, 444262306a36Sopenharmony_ci .fc_type = rtmsg->rtmsg_type, 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci .fc_nlinfo.nl_net = net, 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci .fc_dst = rtmsg->rtmsg_dst, 444762306a36Sopenharmony_ci .fc_src = rtmsg->rtmsg_src, 444862306a36Sopenharmony_ci .fc_gateway = rtmsg->rtmsg_gateway, 444962306a36Sopenharmony_ci }; 445062306a36Sopenharmony_ci} 445162306a36Sopenharmony_ci 445262306a36Sopenharmony_ciint ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg) 445362306a36Sopenharmony_ci{ 445462306a36Sopenharmony_ci struct fib6_config cfg; 445562306a36Sopenharmony_ci int err; 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci if (cmd != SIOCADDRT && cmd != SIOCDELRT) 445862306a36Sopenharmony_ci return -EINVAL; 445962306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 446062306a36Sopenharmony_ci return -EPERM; 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_ci rtmsg_to_fib6_config(net, rtmsg, &cfg); 446362306a36Sopenharmony_ci 446462306a36Sopenharmony_ci rtnl_lock(); 446562306a36Sopenharmony_ci switch (cmd) { 446662306a36Sopenharmony_ci case SIOCADDRT: 446762306a36Sopenharmony_ci err = ip6_route_add(&cfg, GFP_KERNEL, NULL); 446862306a36Sopenharmony_ci break; 446962306a36Sopenharmony_ci case SIOCDELRT: 447062306a36Sopenharmony_ci err = ip6_route_del(&cfg, NULL); 447162306a36Sopenharmony_ci break; 447262306a36Sopenharmony_ci } 447362306a36Sopenharmony_ci rtnl_unlock(); 447462306a36Sopenharmony_ci return err; 447562306a36Sopenharmony_ci} 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_ci/* 447862306a36Sopenharmony_ci * Drop the packet on the floor 447962306a36Sopenharmony_ci */ 448062306a36Sopenharmony_ci 448162306a36Sopenharmony_cistatic int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 448262306a36Sopenharmony_ci{ 448362306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 448462306a36Sopenharmony_ci struct net *net = dev_net(dst->dev); 448562306a36Sopenharmony_ci struct inet6_dev *idev; 448662306a36Sopenharmony_ci SKB_DR(reason); 448762306a36Sopenharmony_ci int type; 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci if (netif_is_l3_master(skb->dev) || 449062306a36Sopenharmony_ci dst->dev == net->loopback_dev) 449162306a36Sopenharmony_ci idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); 449262306a36Sopenharmony_ci else 449362306a36Sopenharmony_ci idev = ip6_dst_idev(dst); 449462306a36Sopenharmony_ci 449562306a36Sopenharmony_ci switch (ipstats_mib_noroutes) { 449662306a36Sopenharmony_ci case IPSTATS_MIB_INNOROUTES: 449762306a36Sopenharmony_ci type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 449862306a36Sopenharmony_ci if (type == IPV6_ADDR_ANY) { 449962306a36Sopenharmony_ci SKB_DR_SET(reason, IP_INADDRERRORS); 450062306a36Sopenharmony_ci IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); 450162306a36Sopenharmony_ci break; 450262306a36Sopenharmony_ci } 450362306a36Sopenharmony_ci SKB_DR_SET(reason, IP_INNOROUTES); 450462306a36Sopenharmony_ci fallthrough; 450562306a36Sopenharmony_ci case IPSTATS_MIB_OUTNOROUTES: 450662306a36Sopenharmony_ci SKB_DR_OR(reason, IP_OUTNOROUTES); 450762306a36Sopenharmony_ci IP6_INC_STATS(net, idev, ipstats_mib_noroutes); 450862306a36Sopenharmony_ci break; 450962306a36Sopenharmony_ci } 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci /* Start over by dropping the dst for l3mdev case */ 451262306a36Sopenharmony_ci if (netif_is_l3_master(skb->dev)) 451362306a36Sopenharmony_ci skb_dst_drop(skb); 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 451662306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 451762306a36Sopenharmony_ci return 0; 451862306a36Sopenharmony_ci} 451962306a36Sopenharmony_ci 452062306a36Sopenharmony_cistatic int ip6_pkt_discard(struct sk_buff *skb) 452162306a36Sopenharmony_ci{ 452262306a36Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 452362306a36Sopenharmony_ci} 452462306a36Sopenharmony_ci 452562306a36Sopenharmony_cistatic int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 452662306a36Sopenharmony_ci{ 452762306a36Sopenharmony_ci skb->dev = skb_dst(skb)->dev; 452862306a36Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 452962306a36Sopenharmony_ci} 453062306a36Sopenharmony_ci 453162306a36Sopenharmony_cistatic int ip6_pkt_prohibit(struct sk_buff *skb) 453262306a36Sopenharmony_ci{ 453362306a36Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 453462306a36Sopenharmony_ci} 453562306a36Sopenharmony_ci 453662306a36Sopenharmony_cistatic int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 453762306a36Sopenharmony_ci{ 453862306a36Sopenharmony_ci skb->dev = skb_dst(skb)->dev; 453962306a36Sopenharmony_ci return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 454062306a36Sopenharmony_ci} 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci/* 454362306a36Sopenharmony_ci * Allocate a dst for local (unicast / anycast) address. 454462306a36Sopenharmony_ci */ 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_cistruct fib6_info *addrconf_f6i_alloc(struct net *net, 454762306a36Sopenharmony_ci struct inet6_dev *idev, 454862306a36Sopenharmony_ci const struct in6_addr *addr, 454962306a36Sopenharmony_ci bool anycast, gfp_t gfp_flags, 455062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 455162306a36Sopenharmony_ci{ 455262306a36Sopenharmony_ci struct fib6_config cfg = { 455362306a36Sopenharmony_ci .fc_table = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL, 455462306a36Sopenharmony_ci .fc_ifindex = idev->dev->ifindex, 455562306a36Sopenharmony_ci .fc_flags = RTF_UP | RTF_NONEXTHOP, 455662306a36Sopenharmony_ci .fc_dst = *addr, 455762306a36Sopenharmony_ci .fc_dst_len = 128, 455862306a36Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 455962306a36Sopenharmony_ci .fc_nlinfo.nl_net = net, 456062306a36Sopenharmony_ci .fc_ignore_dev_down = true, 456162306a36Sopenharmony_ci }; 456262306a36Sopenharmony_ci struct fib6_info *f6i; 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci if (anycast) { 456562306a36Sopenharmony_ci cfg.fc_type = RTN_ANYCAST; 456662306a36Sopenharmony_ci cfg.fc_flags |= RTF_ANYCAST; 456762306a36Sopenharmony_ci } else { 456862306a36Sopenharmony_ci cfg.fc_type = RTN_LOCAL; 456962306a36Sopenharmony_ci cfg.fc_flags |= RTF_LOCAL; 457062306a36Sopenharmony_ci } 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci f6i = ip6_route_info_create(&cfg, gfp_flags, extack); 457362306a36Sopenharmony_ci if (!IS_ERR(f6i)) { 457462306a36Sopenharmony_ci f6i->dst_nocount = true; 457562306a36Sopenharmony_ci 457662306a36Sopenharmony_ci if (!anycast && 457762306a36Sopenharmony_ci (net->ipv6.devconf_all->disable_policy || 457862306a36Sopenharmony_ci idev->cnf.disable_policy)) 457962306a36Sopenharmony_ci f6i->dst_nopolicy = true; 458062306a36Sopenharmony_ci } 458162306a36Sopenharmony_ci 458262306a36Sopenharmony_ci return f6i; 458362306a36Sopenharmony_ci} 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci/* remove deleted ip from prefsrc entries */ 458662306a36Sopenharmony_cistruct arg_dev_net_ip { 458762306a36Sopenharmony_ci struct net *net; 458862306a36Sopenharmony_ci struct in6_addr *addr; 458962306a36Sopenharmony_ci}; 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_cistatic int fib6_remove_prefsrc(struct fib6_info *rt, void *arg) 459262306a36Sopenharmony_ci{ 459362306a36Sopenharmony_ci struct net *net = ((struct arg_dev_net_ip *)arg)->net; 459462306a36Sopenharmony_ci struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 459562306a36Sopenharmony_ci 459662306a36Sopenharmony_ci if (!rt->nh && 459762306a36Sopenharmony_ci rt != net->ipv6.fib6_null_entry && 459862306a36Sopenharmony_ci ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr) && 459962306a36Sopenharmony_ci !ipv6_chk_addr(net, addr, rt->fib6_nh->fib_nh_dev, 0)) { 460062306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 460162306a36Sopenharmony_ci /* remove prefsrc entry */ 460262306a36Sopenharmony_ci rt->fib6_prefsrc.plen = 0; 460362306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 460462306a36Sopenharmony_ci } 460562306a36Sopenharmony_ci return 0; 460662306a36Sopenharmony_ci} 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_civoid rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 460962306a36Sopenharmony_ci{ 461062306a36Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 461162306a36Sopenharmony_ci struct arg_dev_net_ip adni = { 461262306a36Sopenharmony_ci .net = net, 461362306a36Sopenharmony_ci .addr = &ifp->addr, 461462306a36Sopenharmony_ci }; 461562306a36Sopenharmony_ci fib6_clean_all(net, fib6_remove_prefsrc, &adni); 461662306a36Sopenharmony_ci} 461762306a36Sopenharmony_ci 461862306a36Sopenharmony_ci#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT) 461962306a36Sopenharmony_ci 462062306a36Sopenharmony_ci/* Remove routers and update dst entries when gateway turn into host. */ 462162306a36Sopenharmony_cistatic int fib6_clean_tohost(struct fib6_info *rt, void *arg) 462262306a36Sopenharmony_ci{ 462362306a36Sopenharmony_ci struct in6_addr *gateway = (struct in6_addr *)arg; 462462306a36Sopenharmony_ci struct fib6_nh *nh; 462562306a36Sopenharmony_ci 462662306a36Sopenharmony_ci /* RA routes do not use nexthops */ 462762306a36Sopenharmony_ci if (rt->nh) 462862306a36Sopenharmony_ci return 0; 462962306a36Sopenharmony_ci 463062306a36Sopenharmony_ci nh = rt->fib6_nh; 463162306a36Sopenharmony_ci if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) && 463262306a36Sopenharmony_ci nh->fib_nh_gw_family && ipv6_addr_equal(gateway, &nh->fib_nh_gw6)) 463362306a36Sopenharmony_ci return -1; 463462306a36Sopenharmony_ci 463562306a36Sopenharmony_ci /* Further clean up cached routes in exception table. 463662306a36Sopenharmony_ci * This is needed because cached route may have a different 463762306a36Sopenharmony_ci * gateway than its 'parent' in the case of an ip redirect. 463862306a36Sopenharmony_ci */ 463962306a36Sopenharmony_ci fib6_nh_exceptions_clean_tohost(nh, gateway); 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci return 0; 464262306a36Sopenharmony_ci} 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_civoid rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 464562306a36Sopenharmony_ci{ 464662306a36Sopenharmony_ci fib6_clean_all(net, fib6_clean_tohost, gateway); 464762306a36Sopenharmony_ci} 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_cistruct arg_netdev_event { 465062306a36Sopenharmony_ci const struct net_device *dev; 465162306a36Sopenharmony_ci union { 465262306a36Sopenharmony_ci unsigned char nh_flags; 465362306a36Sopenharmony_ci unsigned long event; 465462306a36Sopenharmony_ci }; 465562306a36Sopenharmony_ci}; 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_cistatic struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt) 465862306a36Sopenharmony_ci{ 465962306a36Sopenharmony_ci struct fib6_info *iter; 466062306a36Sopenharmony_ci struct fib6_node *fn; 466162306a36Sopenharmony_ci 466262306a36Sopenharmony_ci fn = rcu_dereference_protected(rt->fib6_node, 466362306a36Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 466462306a36Sopenharmony_ci iter = rcu_dereference_protected(fn->leaf, 466562306a36Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 466662306a36Sopenharmony_ci while (iter) { 466762306a36Sopenharmony_ci if (iter->fib6_metric == rt->fib6_metric && 466862306a36Sopenharmony_ci rt6_qualify_for_ecmp(iter)) 466962306a36Sopenharmony_ci return iter; 467062306a36Sopenharmony_ci iter = rcu_dereference_protected(iter->fib6_next, 467162306a36Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 467262306a36Sopenharmony_ci } 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci return NULL; 467562306a36Sopenharmony_ci} 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_ci/* only called for fib entries with builtin fib6_nh */ 467862306a36Sopenharmony_cistatic bool rt6_is_dead(const struct fib6_info *rt) 467962306a36Sopenharmony_ci{ 468062306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD || 468162306a36Sopenharmony_ci (rt->fib6_nh->fib_nh_flags & RTNH_F_LINKDOWN && 468262306a36Sopenharmony_ci ip6_ignore_linkdown(rt->fib6_nh->fib_nh_dev))) 468362306a36Sopenharmony_ci return true; 468462306a36Sopenharmony_ci 468562306a36Sopenharmony_ci return false; 468662306a36Sopenharmony_ci} 468762306a36Sopenharmony_ci 468862306a36Sopenharmony_cistatic int rt6_multipath_total_weight(const struct fib6_info *rt) 468962306a36Sopenharmony_ci{ 469062306a36Sopenharmony_ci struct fib6_info *iter; 469162306a36Sopenharmony_ci int total = 0; 469262306a36Sopenharmony_ci 469362306a36Sopenharmony_ci if (!rt6_is_dead(rt)) 469462306a36Sopenharmony_ci total += rt->fib6_nh->fib_nh_weight; 469562306a36Sopenharmony_ci 469662306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { 469762306a36Sopenharmony_ci if (!rt6_is_dead(iter)) 469862306a36Sopenharmony_ci total += iter->fib6_nh->fib_nh_weight; 469962306a36Sopenharmony_ci } 470062306a36Sopenharmony_ci 470162306a36Sopenharmony_ci return total; 470262306a36Sopenharmony_ci} 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_cistatic void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total) 470562306a36Sopenharmony_ci{ 470662306a36Sopenharmony_ci int upper_bound = -1; 470762306a36Sopenharmony_ci 470862306a36Sopenharmony_ci if (!rt6_is_dead(rt)) { 470962306a36Sopenharmony_ci *weight += rt->fib6_nh->fib_nh_weight; 471062306a36Sopenharmony_ci upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31, 471162306a36Sopenharmony_ci total) - 1; 471262306a36Sopenharmony_ci } 471362306a36Sopenharmony_ci atomic_set(&rt->fib6_nh->fib_nh_upper_bound, upper_bound); 471462306a36Sopenharmony_ci} 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_cistatic void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total) 471762306a36Sopenharmony_ci{ 471862306a36Sopenharmony_ci struct fib6_info *iter; 471962306a36Sopenharmony_ci int weight = 0; 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci rt6_upper_bound_set(rt, &weight, total); 472262306a36Sopenharmony_ci 472362306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 472462306a36Sopenharmony_ci rt6_upper_bound_set(iter, &weight, total); 472562306a36Sopenharmony_ci} 472662306a36Sopenharmony_ci 472762306a36Sopenharmony_civoid rt6_multipath_rebalance(struct fib6_info *rt) 472862306a36Sopenharmony_ci{ 472962306a36Sopenharmony_ci struct fib6_info *first; 473062306a36Sopenharmony_ci int total; 473162306a36Sopenharmony_ci 473262306a36Sopenharmony_ci /* In case the entire multipath route was marked for flushing, 473362306a36Sopenharmony_ci * then there is no need to rebalance upon the removal of every 473462306a36Sopenharmony_ci * sibling route. 473562306a36Sopenharmony_ci */ 473662306a36Sopenharmony_ci if (!rt->fib6_nsiblings || rt->should_flush) 473762306a36Sopenharmony_ci return; 473862306a36Sopenharmony_ci 473962306a36Sopenharmony_ci /* During lookup routes are evaluated in order, so we need to 474062306a36Sopenharmony_ci * make sure upper bounds are assigned from the first sibling 474162306a36Sopenharmony_ci * onwards. 474262306a36Sopenharmony_ci */ 474362306a36Sopenharmony_ci first = rt6_multipath_first_sibling(rt); 474462306a36Sopenharmony_ci if (WARN_ON_ONCE(!first)) 474562306a36Sopenharmony_ci return; 474662306a36Sopenharmony_ci 474762306a36Sopenharmony_ci total = rt6_multipath_total_weight(first); 474862306a36Sopenharmony_ci rt6_multipath_upper_bound_set(first, total); 474962306a36Sopenharmony_ci} 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_cistatic int fib6_ifup(struct fib6_info *rt, void *p_arg) 475262306a36Sopenharmony_ci{ 475362306a36Sopenharmony_ci const struct arg_netdev_event *arg = p_arg; 475462306a36Sopenharmony_ci struct net *net = dev_net(arg->dev); 475562306a36Sopenharmony_ci 475662306a36Sopenharmony_ci if (rt != net->ipv6.fib6_null_entry && !rt->nh && 475762306a36Sopenharmony_ci rt->fib6_nh->fib_nh_dev == arg->dev) { 475862306a36Sopenharmony_ci rt->fib6_nh->fib_nh_flags &= ~arg->nh_flags; 475962306a36Sopenharmony_ci fib6_update_sernum_upto_root(net, rt); 476062306a36Sopenharmony_ci rt6_multipath_rebalance(rt); 476162306a36Sopenharmony_ci } 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci return 0; 476462306a36Sopenharmony_ci} 476562306a36Sopenharmony_ci 476662306a36Sopenharmony_civoid rt6_sync_up(struct net_device *dev, unsigned char nh_flags) 476762306a36Sopenharmony_ci{ 476862306a36Sopenharmony_ci struct arg_netdev_event arg = { 476962306a36Sopenharmony_ci .dev = dev, 477062306a36Sopenharmony_ci { 477162306a36Sopenharmony_ci .nh_flags = nh_flags, 477262306a36Sopenharmony_ci }, 477362306a36Sopenharmony_ci }; 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev)) 477662306a36Sopenharmony_ci arg.nh_flags |= RTNH_F_LINKDOWN; 477762306a36Sopenharmony_ci 477862306a36Sopenharmony_ci fib6_clean_all(dev_net(dev), fib6_ifup, &arg); 477962306a36Sopenharmony_ci} 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci/* only called for fib entries with inline fib6_nh */ 478262306a36Sopenharmony_cistatic bool rt6_multipath_uses_dev(const struct fib6_info *rt, 478362306a36Sopenharmony_ci const struct net_device *dev) 478462306a36Sopenharmony_ci{ 478562306a36Sopenharmony_ci struct fib6_info *iter; 478662306a36Sopenharmony_ci 478762306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == dev) 478862306a36Sopenharmony_ci return true; 478962306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 479062306a36Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == dev) 479162306a36Sopenharmony_ci return true; 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_ci return false; 479462306a36Sopenharmony_ci} 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_cistatic void rt6_multipath_flush(struct fib6_info *rt) 479762306a36Sopenharmony_ci{ 479862306a36Sopenharmony_ci struct fib6_info *iter; 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_ci rt->should_flush = 1; 480162306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 480262306a36Sopenharmony_ci iter->should_flush = 1; 480362306a36Sopenharmony_ci} 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_cistatic unsigned int rt6_multipath_dead_count(const struct fib6_info *rt, 480662306a36Sopenharmony_ci const struct net_device *down_dev) 480762306a36Sopenharmony_ci{ 480862306a36Sopenharmony_ci struct fib6_info *iter; 480962306a36Sopenharmony_ci unsigned int dead = 0; 481062306a36Sopenharmony_ci 481162306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == down_dev || 481262306a36Sopenharmony_ci rt->fib6_nh->fib_nh_flags & RTNH_F_DEAD) 481362306a36Sopenharmony_ci dead++; 481462306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 481562306a36Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == down_dev || 481662306a36Sopenharmony_ci iter->fib6_nh->fib_nh_flags & RTNH_F_DEAD) 481762306a36Sopenharmony_ci dead++; 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci return dead; 482062306a36Sopenharmony_ci} 482162306a36Sopenharmony_ci 482262306a36Sopenharmony_cistatic void rt6_multipath_nh_flags_set(struct fib6_info *rt, 482362306a36Sopenharmony_ci const struct net_device *dev, 482462306a36Sopenharmony_ci unsigned char nh_flags) 482562306a36Sopenharmony_ci{ 482662306a36Sopenharmony_ci struct fib6_info *iter; 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev == dev) 482962306a36Sopenharmony_ci rt->fib6_nh->fib_nh_flags |= nh_flags; 483062306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 483162306a36Sopenharmony_ci if (iter->fib6_nh->fib_nh_dev == dev) 483262306a36Sopenharmony_ci iter->fib6_nh->fib_nh_flags |= nh_flags; 483362306a36Sopenharmony_ci} 483462306a36Sopenharmony_ci 483562306a36Sopenharmony_ci/* called with write lock held for table with rt */ 483662306a36Sopenharmony_cistatic int fib6_ifdown(struct fib6_info *rt, void *p_arg) 483762306a36Sopenharmony_ci{ 483862306a36Sopenharmony_ci const struct arg_netdev_event *arg = p_arg; 483962306a36Sopenharmony_ci const struct net_device *dev = arg->dev; 484062306a36Sopenharmony_ci struct net *net = dev_net(dev); 484162306a36Sopenharmony_ci 484262306a36Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry || rt->nh) 484362306a36Sopenharmony_ci return 0; 484462306a36Sopenharmony_ci 484562306a36Sopenharmony_ci switch (arg->event) { 484662306a36Sopenharmony_ci case NETDEV_UNREGISTER: 484762306a36Sopenharmony_ci return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0; 484862306a36Sopenharmony_ci case NETDEV_DOWN: 484962306a36Sopenharmony_ci if (rt->should_flush) 485062306a36Sopenharmony_ci return -1; 485162306a36Sopenharmony_ci if (!rt->fib6_nsiblings) 485262306a36Sopenharmony_ci return rt->fib6_nh->fib_nh_dev == dev ? -1 : 0; 485362306a36Sopenharmony_ci if (rt6_multipath_uses_dev(rt, dev)) { 485462306a36Sopenharmony_ci unsigned int count; 485562306a36Sopenharmony_ci 485662306a36Sopenharmony_ci count = rt6_multipath_dead_count(rt, dev); 485762306a36Sopenharmony_ci if (rt->fib6_nsiblings + 1 == count) { 485862306a36Sopenharmony_ci rt6_multipath_flush(rt); 485962306a36Sopenharmony_ci return -1; 486062306a36Sopenharmony_ci } 486162306a36Sopenharmony_ci rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD | 486262306a36Sopenharmony_ci RTNH_F_LINKDOWN); 486362306a36Sopenharmony_ci fib6_update_sernum(net, rt); 486462306a36Sopenharmony_ci rt6_multipath_rebalance(rt); 486562306a36Sopenharmony_ci } 486662306a36Sopenharmony_ci return -2; 486762306a36Sopenharmony_ci case NETDEV_CHANGE: 486862306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev != dev || 486962306a36Sopenharmony_ci rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) 487062306a36Sopenharmony_ci break; 487162306a36Sopenharmony_ci rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 487262306a36Sopenharmony_ci rt6_multipath_rebalance(rt); 487362306a36Sopenharmony_ci break; 487462306a36Sopenharmony_ci } 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci return 0; 487762306a36Sopenharmony_ci} 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_civoid rt6_sync_down_dev(struct net_device *dev, unsigned long event) 488062306a36Sopenharmony_ci{ 488162306a36Sopenharmony_ci struct arg_netdev_event arg = { 488262306a36Sopenharmony_ci .dev = dev, 488362306a36Sopenharmony_ci { 488462306a36Sopenharmony_ci .event = event, 488562306a36Sopenharmony_ci }, 488662306a36Sopenharmony_ci }; 488762306a36Sopenharmony_ci struct net *net = dev_net(dev); 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci if (net->ipv6.sysctl.skip_notify_on_dev_down) 489062306a36Sopenharmony_ci fib6_clean_all_skip_notify(net, fib6_ifdown, &arg); 489162306a36Sopenharmony_ci else 489262306a36Sopenharmony_ci fib6_clean_all(net, fib6_ifdown, &arg); 489362306a36Sopenharmony_ci} 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_civoid rt6_disable_ip(struct net_device *dev, unsigned long event) 489662306a36Sopenharmony_ci{ 489762306a36Sopenharmony_ci rt6_sync_down_dev(dev, event); 489862306a36Sopenharmony_ci rt6_uncached_list_flush_dev(dev); 489962306a36Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 490062306a36Sopenharmony_ci} 490162306a36Sopenharmony_ci 490262306a36Sopenharmony_cistruct rt6_mtu_change_arg { 490362306a36Sopenharmony_ci struct net_device *dev; 490462306a36Sopenharmony_ci unsigned int mtu; 490562306a36Sopenharmony_ci struct fib6_info *f6i; 490662306a36Sopenharmony_ci}; 490762306a36Sopenharmony_ci 490862306a36Sopenharmony_cistatic int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg) 490962306a36Sopenharmony_ci{ 491062306a36Sopenharmony_ci struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg; 491162306a36Sopenharmony_ci struct fib6_info *f6i = arg->f6i; 491262306a36Sopenharmony_ci 491362306a36Sopenharmony_ci /* For administrative MTU increase, there is no way to discover 491462306a36Sopenharmony_ci * IPv6 PMTU increase, so PMTU increase should be updated here. 491562306a36Sopenharmony_ci * Since RFC 1981 doesn't include administrative MTU increase 491662306a36Sopenharmony_ci * update PMTU increase is a MUST. (i.e. jumbo frame) 491762306a36Sopenharmony_ci */ 491862306a36Sopenharmony_ci if (nh->fib_nh_dev == arg->dev) { 491962306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(arg->dev); 492062306a36Sopenharmony_ci u32 mtu = f6i->fib6_pmtu; 492162306a36Sopenharmony_ci 492262306a36Sopenharmony_ci if (mtu >= arg->mtu || 492362306a36Sopenharmony_ci (mtu < arg->mtu && mtu == idev->cnf.mtu6)) 492462306a36Sopenharmony_ci fib6_metric_set(f6i, RTAX_MTU, arg->mtu); 492562306a36Sopenharmony_ci 492662306a36Sopenharmony_ci spin_lock_bh(&rt6_exception_lock); 492762306a36Sopenharmony_ci rt6_exceptions_update_pmtu(idev, nh, arg->mtu); 492862306a36Sopenharmony_ci spin_unlock_bh(&rt6_exception_lock); 492962306a36Sopenharmony_ci } 493062306a36Sopenharmony_ci 493162306a36Sopenharmony_ci return 0; 493262306a36Sopenharmony_ci} 493362306a36Sopenharmony_ci 493462306a36Sopenharmony_cistatic int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg) 493562306a36Sopenharmony_ci{ 493662306a36Sopenharmony_ci struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 493762306a36Sopenharmony_ci struct inet6_dev *idev; 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci /* In IPv6 pmtu discovery is not optional, 494062306a36Sopenharmony_ci so that RTAX_MTU lock cannot disable it. 494162306a36Sopenharmony_ci We still use this lock to block changes 494262306a36Sopenharmony_ci caused by addrconf/ndisc. 494362306a36Sopenharmony_ci */ 494462306a36Sopenharmony_ci 494562306a36Sopenharmony_ci idev = __in6_dev_get(arg->dev); 494662306a36Sopenharmony_ci if (!idev) 494762306a36Sopenharmony_ci return 0; 494862306a36Sopenharmony_ci 494962306a36Sopenharmony_ci if (fib6_metric_locked(f6i, RTAX_MTU)) 495062306a36Sopenharmony_ci return 0; 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci arg->f6i = f6i; 495362306a36Sopenharmony_ci if (f6i->nh) { 495462306a36Sopenharmony_ci /* fib6_nh_mtu_change only returns 0, so this is safe */ 495562306a36Sopenharmony_ci return nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_mtu_change, 495662306a36Sopenharmony_ci arg); 495762306a36Sopenharmony_ci } 495862306a36Sopenharmony_ci 495962306a36Sopenharmony_ci return fib6_nh_mtu_change(f6i->fib6_nh, arg); 496062306a36Sopenharmony_ci} 496162306a36Sopenharmony_ci 496262306a36Sopenharmony_civoid rt6_mtu_change(struct net_device *dev, unsigned int mtu) 496362306a36Sopenharmony_ci{ 496462306a36Sopenharmony_ci struct rt6_mtu_change_arg arg = { 496562306a36Sopenharmony_ci .dev = dev, 496662306a36Sopenharmony_ci .mtu = mtu, 496762306a36Sopenharmony_ci }; 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 497062306a36Sopenharmony_ci} 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_cistatic const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 497362306a36Sopenharmony_ci [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 }, 497462306a36Sopenharmony_ci [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 497562306a36Sopenharmony_ci [RTA_PREFSRC] = { .len = sizeof(struct in6_addr) }, 497662306a36Sopenharmony_ci [RTA_OIF] = { .type = NLA_U32 }, 497762306a36Sopenharmony_ci [RTA_IIF] = { .type = NLA_U32 }, 497862306a36Sopenharmony_ci [RTA_PRIORITY] = { .type = NLA_U32 }, 497962306a36Sopenharmony_ci [RTA_METRICS] = { .type = NLA_NESTED }, 498062306a36Sopenharmony_ci [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 498162306a36Sopenharmony_ci [RTA_PREF] = { .type = NLA_U8 }, 498262306a36Sopenharmony_ci [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 498362306a36Sopenharmony_ci [RTA_ENCAP] = { .type = NLA_NESTED }, 498462306a36Sopenharmony_ci [RTA_EXPIRES] = { .type = NLA_U32 }, 498562306a36Sopenharmony_ci [RTA_UID] = { .type = NLA_U32 }, 498662306a36Sopenharmony_ci [RTA_MARK] = { .type = NLA_U32 }, 498762306a36Sopenharmony_ci [RTA_TABLE] = { .type = NLA_U32 }, 498862306a36Sopenharmony_ci [RTA_IP_PROTO] = { .type = NLA_U8 }, 498962306a36Sopenharmony_ci [RTA_SPORT] = { .type = NLA_U16 }, 499062306a36Sopenharmony_ci [RTA_DPORT] = { .type = NLA_U16 }, 499162306a36Sopenharmony_ci [RTA_NH_ID] = { .type = NLA_U32 }, 499262306a36Sopenharmony_ci}; 499362306a36Sopenharmony_ci 499462306a36Sopenharmony_cistatic int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 499562306a36Sopenharmony_ci struct fib6_config *cfg, 499662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 499762306a36Sopenharmony_ci{ 499862306a36Sopenharmony_ci struct rtmsg *rtm; 499962306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX+1]; 500062306a36Sopenharmony_ci unsigned int pref; 500162306a36Sopenharmony_ci int err; 500262306a36Sopenharmony_ci 500362306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 500462306a36Sopenharmony_ci rtm_ipv6_policy, extack); 500562306a36Sopenharmony_ci if (err < 0) 500662306a36Sopenharmony_ci goto errout; 500762306a36Sopenharmony_ci 500862306a36Sopenharmony_ci err = -EINVAL; 500962306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 501062306a36Sopenharmony_ci 501162306a36Sopenharmony_ci if (rtm->rtm_tos) { 501262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 501362306a36Sopenharmony_ci "Invalid dsfield (tos): option not available for IPv6"); 501462306a36Sopenharmony_ci goto errout; 501562306a36Sopenharmony_ci } 501662306a36Sopenharmony_ci 501762306a36Sopenharmony_ci *cfg = (struct fib6_config){ 501862306a36Sopenharmony_ci .fc_table = rtm->rtm_table, 501962306a36Sopenharmony_ci .fc_dst_len = rtm->rtm_dst_len, 502062306a36Sopenharmony_ci .fc_src_len = rtm->rtm_src_len, 502162306a36Sopenharmony_ci .fc_flags = RTF_UP, 502262306a36Sopenharmony_ci .fc_protocol = rtm->rtm_protocol, 502362306a36Sopenharmony_ci .fc_type = rtm->rtm_type, 502462306a36Sopenharmony_ci 502562306a36Sopenharmony_ci .fc_nlinfo.portid = NETLINK_CB(skb).portid, 502662306a36Sopenharmony_ci .fc_nlinfo.nlh = nlh, 502762306a36Sopenharmony_ci .fc_nlinfo.nl_net = sock_net(skb->sk), 502862306a36Sopenharmony_ci }; 502962306a36Sopenharmony_ci 503062306a36Sopenharmony_ci if (rtm->rtm_type == RTN_UNREACHABLE || 503162306a36Sopenharmony_ci rtm->rtm_type == RTN_BLACKHOLE || 503262306a36Sopenharmony_ci rtm->rtm_type == RTN_PROHIBIT || 503362306a36Sopenharmony_ci rtm->rtm_type == RTN_THROW) 503462306a36Sopenharmony_ci cfg->fc_flags |= RTF_REJECT; 503562306a36Sopenharmony_ci 503662306a36Sopenharmony_ci if (rtm->rtm_type == RTN_LOCAL) 503762306a36Sopenharmony_ci cfg->fc_flags |= RTF_LOCAL; 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_ci if (rtm->rtm_flags & RTM_F_CLONED) 504062306a36Sopenharmony_ci cfg->fc_flags |= RTF_CACHE; 504162306a36Sopenharmony_ci 504262306a36Sopenharmony_ci cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci if (tb[RTA_NH_ID]) { 504562306a36Sopenharmony_ci if (tb[RTA_GATEWAY] || tb[RTA_OIF] || 504662306a36Sopenharmony_ci tb[RTA_MULTIPATH] || tb[RTA_ENCAP]) { 504762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 504862306a36Sopenharmony_ci "Nexthop specification and nexthop id are mutually exclusive"); 504962306a36Sopenharmony_ci goto errout; 505062306a36Sopenharmony_ci } 505162306a36Sopenharmony_ci cfg->fc_nh_id = nla_get_u32(tb[RTA_NH_ID]); 505262306a36Sopenharmony_ci } 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci if (tb[RTA_GATEWAY]) { 505562306a36Sopenharmony_ci cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 505662306a36Sopenharmony_ci cfg->fc_flags |= RTF_GATEWAY; 505762306a36Sopenharmony_ci } 505862306a36Sopenharmony_ci if (tb[RTA_VIA]) { 505962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 does not support RTA_VIA attribute"); 506062306a36Sopenharmony_ci goto errout; 506162306a36Sopenharmony_ci } 506262306a36Sopenharmony_ci 506362306a36Sopenharmony_ci if (tb[RTA_DST]) { 506462306a36Sopenharmony_ci int plen = (rtm->rtm_dst_len + 7) >> 3; 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_ci if (nla_len(tb[RTA_DST]) < plen) 506762306a36Sopenharmony_ci goto errout; 506862306a36Sopenharmony_ci 506962306a36Sopenharmony_ci nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 507062306a36Sopenharmony_ci } 507162306a36Sopenharmony_ci 507262306a36Sopenharmony_ci if (tb[RTA_SRC]) { 507362306a36Sopenharmony_ci int plen = (rtm->rtm_src_len + 7) >> 3; 507462306a36Sopenharmony_ci 507562306a36Sopenharmony_ci if (nla_len(tb[RTA_SRC]) < plen) 507662306a36Sopenharmony_ci goto errout; 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 507962306a36Sopenharmony_ci } 508062306a36Sopenharmony_ci 508162306a36Sopenharmony_ci if (tb[RTA_PREFSRC]) 508262306a36Sopenharmony_ci cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 508362306a36Sopenharmony_ci 508462306a36Sopenharmony_ci if (tb[RTA_OIF]) 508562306a36Sopenharmony_ci cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 508662306a36Sopenharmony_ci 508762306a36Sopenharmony_ci if (tb[RTA_PRIORITY]) 508862306a36Sopenharmony_ci cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 508962306a36Sopenharmony_ci 509062306a36Sopenharmony_ci if (tb[RTA_METRICS]) { 509162306a36Sopenharmony_ci cfg->fc_mx = nla_data(tb[RTA_METRICS]); 509262306a36Sopenharmony_ci cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 509362306a36Sopenharmony_ci } 509462306a36Sopenharmony_ci 509562306a36Sopenharmony_ci if (tb[RTA_TABLE]) 509662306a36Sopenharmony_ci cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_ci if (tb[RTA_MULTIPATH]) { 509962306a36Sopenharmony_ci cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 510062306a36Sopenharmony_ci cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 510162306a36Sopenharmony_ci 510262306a36Sopenharmony_ci err = lwtunnel_valid_encap_type_attr(cfg->fc_mp, 510362306a36Sopenharmony_ci cfg->fc_mp_len, extack); 510462306a36Sopenharmony_ci if (err < 0) 510562306a36Sopenharmony_ci goto errout; 510662306a36Sopenharmony_ci } 510762306a36Sopenharmony_ci 510862306a36Sopenharmony_ci if (tb[RTA_PREF]) { 510962306a36Sopenharmony_ci pref = nla_get_u8(tb[RTA_PREF]); 511062306a36Sopenharmony_ci if (pref != ICMPV6_ROUTER_PREF_LOW && 511162306a36Sopenharmony_ci pref != ICMPV6_ROUTER_PREF_HIGH) 511262306a36Sopenharmony_ci pref = ICMPV6_ROUTER_PREF_MEDIUM; 511362306a36Sopenharmony_ci cfg->fc_flags |= RTF_PREF(pref); 511462306a36Sopenharmony_ci } 511562306a36Sopenharmony_ci 511662306a36Sopenharmony_ci if (tb[RTA_ENCAP]) 511762306a36Sopenharmony_ci cfg->fc_encap = tb[RTA_ENCAP]; 511862306a36Sopenharmony_ci 511962306a36Sopenharmony_ci if (tb[RTA_ENCAP_TYPE]) { 512062306a36Sopenharmony_ci cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 512162306a36Sopenharmony_ci 512262306a36Sopenharmony_ci err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack); 512362306a36Sopenharmony_ci if (err < 0) 512462306a36Sopenharmony_ci goto errout; 512562306a36Sopenharmony_ci } 512662306a36Sopenharmony_ci 512762306a36Sopenharmony_ci if (tb[RTA_EXPIRES]) { 512862306a36Sopenharmony_ci unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 513162306a36Sopenharmony_ci cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 513262306a36Sopenharmony_ci cfg->fc_flags |= RTF_EXPIRES; 513362306a36Sopenharmony_ci } 513462306a36Sopenharmony_ci } 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci err = 0; 513762306a36Sopenharmony_cierrout: 513862306a36Sopenharmony_ci return err; 513962306a36Sopenharmony_ci} 514062306a36Sopenharmony_ci 514162306a36Sopenharmony_cistruct rt6_nh { 514262306a36Sopenharmony_ci struct fib6_info *fib6_info; 514362306a36Sopenharmony_ci struct fib6_config r_cfg; 514462306a36Sopenharmony_ci struct list_head next; 514562306a36Sopenharmony_ci}; 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_cistatic int ip6_route_info_append(struct net *net, 514862306a36Sopenharmony_ci struct list_head *rt6_nh_list, 514962306a36Sopenharmony_ci struct fib6_info *rt, 515062306a36Sopenharmony_ci struct fib6_config *r_cfg) 515162306a36Sopenharmony_ci{ 515262306a36Sopenharmony_ci struct rt6_nh *nh; 515362306a36Sopenharmony_ci int err = -EEXIST; 515462306a36Sopenharmony_ci 515562306a36Sopenharmony_ci list_for_each_entry(nh, rt6_nh_list, next) { 515662306a36Sopenharmony_ci /* check if fib6_info already exists */ 515762306a36Sopenharmony_ci if (rt6_duplicate_nexthop(nh->fib6_info, rt)) 515862306a36Sopenharmony_ci return err; 515962306a36Sopenharmony_ci } 516062306a36Sopenharmony_ci 516162306a36Sopenharmony_ci nh = kzalloc(sizeof(*nh), GFP_KERNEL); 516262306a36Sopenharmony_ci if (!nh) 516362306a36Sopenharmony_ci return -ENOMEM; 516462306a36Sopenharmony_ci nh->fib6_info = rt; 516562306a36Sopenharmony_ci memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 516662306a36Sopenharmony_ci list_add_tail(&nh->next, rt6_nh_list); 516762306a36Sopenharmony_ci 516862306a36Sopenharmony_ci return 0; 516962306a36Sopenharmony_ci} 517062306a36Sopenharmony_ci 517162306a36Sopenharmony_cistatic void ip6_route_mpath_notify(struct fib6_info *rt, 517262306a36Sopenharmony_ci struct fib6_info *rt_last, 517362306a36Sopenharmony_ci struct nl_info *info, 517462306a36Sopenharmony_ci __u16 nlflags) 517562306a36Sopenharmony_ci{ 517662306a36Sopenharmony_ci /* if this is an APPEND route, then rt points to the first route 517762306a36Sopenharmony_ci * inserted and rt_last points to last route inserted. Userspace 517862306a36Sopenharmony_ci * wants a consistent dump of the route which starts at the first 517962306a36Sopenharmony_ci * nexthop. Since sibling routes are always added at the end of 518062306a36Sopenharmony_ci * the list, find the first sibling of the last route appended 518162306a36Sopenharmony_ci */ 518262306a36Sopenharmony_ci if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) { 518362306a36Sopenharmony_ci rt = list_first_entry(&rt_last->fib6_siblings, 518462306a36Sopenharmony_ci struct fib6_info, 518562306a36Sopenharmony_ci fib6_siblings); 518662306a36Sopenharmony_ci } 518762306a36Sopenharmony_ci 518862306a36Sopenharmony_ci if (rt) 518962306a36Sopenharmony_ci inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 519062306a36Sopenharmony_ci} 519162306a36Sopenharmony_ci 519262306a36Sopenharmony_cistatic bool ip6_route_mpath_should_notify(const struct fib6_info *rt) 519362306a36Sopenharmony_ci{ 519462306a36Sopenharmony_ci bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); 519562306a36Sopenharmony_ci bool should_notify = false; 519662306a36Sopenharmony_ci struct fib6_info *leaf; 519762306a36Sopenharmony_ci struct fib6_node *fn; 519862306a36Sopenharmony_ci 519962306a36Sopenharmony_ci rcu_read_lock(); 520062306a36Sopenharmony_ci fn = rcu_dereference(rt->fib6_node); 520162306a36Sopenharmony_ci if (!fn) 520262306a36Sopenharmony_ci goto out; 520362306a36Sopenharmony_ci 520462306a36Sopenharmony_ci leaf = rcu_dereference(fn->leaf); 520562306a36Sopenharmony_ci if (!leaf) 520662306a36Sopenharmony_ci goto out; 520762306a36Sopenharmony_ci 520862306a36Sopenharmony_ci if (rt == leaf || 520962306a36Sopenharmony_ci (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric && 521062306a36Sopenharmony_ci rt6_qualify_for_ecmp(leaf))) 521162306a36Sopenharmony_ci should_notify = true; 521262306a36Sopenharmony_ciout: 521362306a36Sopenharmony_ci rcu_read_unlock(); 521462306a36Sopenharmony_ci 521562306a36Sopenharmony_ci return should_notify; 521662306a36Sopenharmony_ci} 521762306a36Sopenharmony_ci 521862306a36Sopenharmony_cistatic int fib6_gw_from_attr(struct in6_addr *gw, struct nlattr *nla, 521962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 522062306a36Sopenharmony_ci{ 522162306a36Sopenharmony_ci if (nla_len(nla) < sizeof(*gw)) { 522262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_GATEWAY"); 522362306a36Sopenharmony_ci return -EINVAL; 522462306a36Sopenharmony_ci } 522562306a36Sopenharmony_ci 522662306a36Sopenharmony_ci *gw = nla_get_in6_addr(nla); 522762306a36Sopenharmony_ci 522862306a36Sopenharmony_ci return 0; 522962306a36Sopenharmony_ci} 523062306a36Sopenharmony_ci 523162306a36Sopenharmony_cistatic int ip6_route_multipath_add(struct fib6_config *cfg, 523262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 523362306a36Sopenharmony_ci{ 523462306a36Sopenharmony_ci struct fib6_info *rt_notif = NULL, *rt_last = NULL; 523562306a36Sopenharmony_ci struct nl_info *info = &cfg->fc_nlinfo; 523662306a36Sopenharmony_ci struct fib6_config r_cfg; 523762306a36Sopenharmony_ci struct rtnexthop *rtnh; 523862306a36Sopenharmony_ci struct fib6_info *rt; 523962306a36Sopenharmony_ci struct rt6_nh *err_nh; 524062306a36Sopenharmony_ci struct rt6_nh *nh, *nh_safe; 524162306a36Sopenharmony_ci __u16 nlflags; 524262306a36Sopenharmony_ci int remaining; 524362306a36Sopenharmony_ci int attrlen; 524462306a36Sopenharmony_ci int err = 1; 524562306a36Sopenharmony_ci int nhn = 0; 524662306a36Sopenharmony_ci int replace = (cfg->fc_nlinfo.nlh && 524762306a36Sopenharmony_ci (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 524862306a36Sopenharmony_ci LIST_HEAD(rt6_nh_list); 524962306a36Sopenharmony_ci 525062306a36Sopenharmony_ci nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 525162306a36Sopenharmony_ci if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 525262306a36Sopenharmony_ci nlflags |= NLM_F_APPEND; 525362306a36Sopenharmony_ci 525462306a36Sopenharmony_ci remaining = cfg->fc_mp_len; 525562306a36Sopenharmony_ci rtnh = (struct rtnexthop *)cfg->fc_mp; 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci /* Parse a Multipath Entry and build a list (rt6_nh_list) of 525862306a36Sopenharmony_ci * fib6_info structs per nexthop 525962306a36Sopenharmony_ci */ 526062306a36Sopenharmony_ci while (rtnh_ok(rtnh, remaining)) { 526162306a36Sopenharmony_ci memcpy(&r_cfg, cfg, sizeof(*cfg)); 526262306a36Sopenharmony_ci if (rtnh->rtnh_ifindex) 526362306a36Sopenharmony_ci r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci attrlen = rtnh_attrlen(rtnh); 526662306a36Sopenharmony_ci if (attrlen > 0) { 526762306a36Sopenharmony_ci struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 526862306a36Sopenharmony_ci 526962306a36Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_GATEWAY); 527062306a36Sopenharmony_ci if (nla) { 527162306a36Sopenharmony_ci err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla, 527262306a36Sopenharmony_ci extack); 527362306a36Sopenharmony_ci if (err) 527462306a36Sopenharmony_ci goto cleanup; 527562306a36Sopenharmony_ci 527662306a36Sopenharmony_ci r_cfg.fc_flags |= RTF_GATEWAY; 527762306a36Sopenharmony_ci } 527862306a36Sopenharmony_ci r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 527962306a36Sopenharmony_ci 528062306a36Sopenharmony_ci /* RTA_ENCAP_TYPE length checked in 528162306a36Sopenharmony_ci * lwtunnel_valid_encap_type_attr 528262306a36Sopenharmony_ci */ 528362306a36Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 528462306a36Sopenharmony_ci if (nla) 528562306a36Sopenharmony_ci r_cfg.fc_encap_type = nla_get_u16(nla); 528662306a36Sopenharmony_ci } 528762306a36Sopenharmony_ci 528862306a36Sopenharmony_ci r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 528962306a36Sopenharmony_ci rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack); 529062306a36Sopenharmony_ci if (IS_ERR(rt)) { 529162306a36Sopenharmony_ci err = PTR_ERR(rt); 529262306a36Sopenharmony_ci rt = NULL; 529362306a36Sopenharmony_ci goto cleanup; 529462306a36Sopenharmony_ci } 529562306a36Sopenharmony_ci if (!rt6_qualify_for_ecmp(rt)) { 529662306a36Sopenharmony_ci err = -EINVAL; 529762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 529862306a36Sopenharmony_ci "Device only routes can not be added for IPv6 using the multipath API."); 529962306a36Sopenharmony_ci fib6_info_release(rt); 530062306a36Sopenharmony_ci goto cleanup; 530162306a36Sopenharmony_ci } 530262306a36Sopenharmony_ci 530362306a36Sopenharmony_ci rt->fib6_nh->fib_nh_weight = rtnh->rtnh_hops + 1; 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_ci err = ip6_route_info_append(info->nl_net, &rt6_nh_list, 530662306a36Sopenharmony_ci rt, &r_cfg); 530762306a36Sopenharmony_ci if (err) { 530862306a36Sopenharmony_ci fib6_info_release(rt); 530962306a36Sopenharmony_ci goto cleanup; 531062306a36Sopenharmony_ci } 531162306a36Sopenharmony_ci 531262306a36Sopenharmony_ci rtnh = rtnh_next(rtnh, &remaining); 531362306a36Sopenharmony_ci } 531462306a36Sopenharmony_ci 531562306a36Sopenharmony_ci if (list_empty(&rt6_nh_list)) { 531662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 531762306a36Sopenharmony_ci "Invalid nexthop configuration - no valid nexthops"); 531862306a36Sopenharmony_ci return -EINVAL; 531962306a36Sopenharmony_ci } 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci /* for add and replace send one notification with all nexthops. 532262306a36Sopenharmony_ci * Skip the notification in fib6_add_rt2node and send one with 532362306a36Sopenharmony_ci * the full route when done 532462306a36Sopenharmony_ci */ 532562306a36Sopenharmony_ci info->skip_notify = 1; 532662306a36Sopenharmony_ci 532762306a36Sopenharmony_ci /* For add and replace, send one notification with all nexthops. For 532862306a36Sopenharmony_ci * append, send one notification with all appended nexthops. 532962306a36Sopenharmony_ci */ 533062306a36Sopenharmony_ci info->skip_notify_kernel = 1; 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci err_nh = NULL; 533362306a36Sopenharmony_ci list_for_each_entry(nh, &rt6_nh_list, next) { 533462306a36Sopenharmony_ci err = __ip6_ins_rt(nh->fib6_info, info, extack); 533562306a36Sopenharmony_ci 533662306a36Sopenharmony_ci if (err) { 533762306a36Sopenharmony_ci if (replace && nhn) 533862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 533962306a36Sopenharmony_ci "multipath route replace failed (check consistency of installed routes)"); 534062306a36Sopenharmony_ci err_nh = nh; 534162306a36Sopenharmony_ci goto add_errout; 534262306a36Sopenharmony_ci } 534362306a36Sopenharmony_ci /* save reference to last route successfully inserted */ 534462306a36Sopenharmony_ci rt_last = nh->fib6_info; 534562306a36Sopenharmony_ci 534662306a36Sopenharmony_ci /* save reference to first route for notification */ 534762306a36Sopenharmony_ci if (!rt_notif) 534862306a36Sopenharmony_ci rt_notif = nh->fib6_info; 534962306a36Sopenharmony_ci 535062306a36Sopenharmony_ci /* Because each route is added like a single route we remove 535162306a36Sopenharmony_ci * these flags after the first nexthop: if there is a collision, 535262306a36Sopenharmony_ci * we have already failed to add the first nexthop: 535362306a36Sopenharmony_ci * fib6_add_rt2node() has rejected it; when replacing, old 535462306a36Sopenharmony_ci * nexthops have been replaced by first new, the rest should 535562306a36Sopenharmony_ci * be added to it. 535662306a36Sopenharmony_ci */ 535762306a36Sopenharmony_ci if (cfg->fc_nlinfo.nlh) { 535862306a36Sopenharmony_ci cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 535962306a36Sopenharmony_ci NLM_F_REPLACE); 536062306a36Sopenharmony_ci cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE; 536162306a36Sopenharmony_ci } 536262306a36Sopenharmony_ci nhn++; 536362306a36Sopenharmony_ci } 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci /* An in-kernel notification should only be sent in case the new 536662306a36Sopenharmony_ci * multipath route is added as the first route in the node, or if 536762306a36Sopenharmony_ci * it was appended to it. We pass 'rt_notif' since it is the first 536862306a36Sopenharmony_ci * sibling and might allow us to skip some checks in the replace case. 536962306a36Sopenharmony_ci */ 537062306a36Sopenharmony_ci if (ip6_route_mpath_should_notify(rt_notif)) { 537162306a36Sopenharmony_ci enum fib_event_type fib_event; 537262306a36Sopenharmony_ci 537362306a36Sopenharmony_ci if (rt_notif->fib6_nsiblings != nhn - 1) 537462306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_APPEND; 537562306a36Sopenharmony_ci else 537662306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 537762306a36Sopenharmony_ci 537862306a36Sopenharmony_ci err = call_fib6_multipath_entry_notifiers(info->nl_net, 537962306a36Sopenharmony_ci fib_event, rt_notif, 538062306a36Sopenharmony_ci nhn - 1, extack); 538162306a36Sopenharmony_ci if (err) { 538262306a36Sopenharmony_ci /* Delete all the siblings that were just added */ 538362306a36Sopenharmony_ci err_nh = NULL; 538462306a36Sopenharmony_ci goto add_errout; 538562306a36Sopenharmony_ci } 538662306a36Sopenharmony_ci } 538762306a36Sopenharmony_ci 538862306a36Sopenharmony_ci /* success ... tell user about new route */ 538962306a36Sopenharmony_ci ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 539062306a36Sopenharmony_ci goto cleanup; 539162306a36Sopenharmony_ci 539262306a36Sopenharmony_ciadd_errout: 539362306a36Sopenharmony_ci /* send notification for routes that were added so that 539462306a36Sopenharmony_ci * the delete notifications sent by ip6_route_del are 539562306a36Sopenharmony_ci * coherent 539662306a36Sopenharmony_ci */ 539762306a36Sopenharmony_ci if (rt_notif) 539862306a36Sopenharmony_ci ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 539962306a36Sopenharmony_ci 540062306a36Sopenharmony_ci /* Delete routes that were already added */ 540162306a36Sopenharmony_ci list_for_each_entry(nh, &rt6_nh_list, next) { 540262306a36Sopenharmony_ci if (err_nh == nh) 540362306a36Sopenharmony_ci break; 540462306a36Sopenharmony_ci ip6_route_del(&nh->r_cfg, extack); 540562306a36Sopenharmony_ci } 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_cicleanup: 540862306a36Sopenharmony_ci list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 540962306a36Sopenharmony_ci fib6_info_release(nh->fib6_info); 541062306a36Sopenharmony_ci list_del(&nh->next); 541162306a36Sopenharmony_ci kfree(nh); 541262306a36Sopenharmony_ci } 541362306a36Sopenharmony_ci 541462306a36Sopenharmony_ci return err; 541562306a36Sopenharmony_ci} 541662306a36Sopenharmony_ci 541762306a36Sopenharmony_cistatic int ip6_route_multipath_del(struct fib6_config *cfg, 541862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 541962306a36Sopenharmony_ci{ 542062306a36Sopenharmony_ci struct fib6_config r_cfg; 542162306a36Sopenharmony_ci struct rtnexthop *rtnh; 542262306a36Sopenharmony_ci int last_err = 0; 542362306a36Sopenharmony_ci int remaining; 542462306a36Sopenharmony_ci int attrlen; 542562306a36Sopenharmony_ci int err; 542662306a36Sopenharmony_ci 542762306a36Sopenharmony_ci remaining = cfg->fc_mp_len; 542862306a36Sopenharmony_ci rtnh = (struct rtnexthop *)cfg->fc_mp; 542962306a36Sopenharmony_ci 543062306a36Sopenharmony_ci /* Parse a Multipath Entry */ 543162306a36Sopenharmony_ci while (rtnh_ok(rtnh, remaining)) { 543262306a36Sopenharmony_ci memcpy(&r_cfg, cfg, sizeof(*cfg)); 543362306a36Sopenharmony_ci if (rtnh->rtnh_ifindex) 543462306a36Sopenharmony_ci r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 543562306a36Sopenharmony_ci 543662306a36Sopenharmony_ci attrlen = rtnh_attrlen(rtnh); 543762306a36Sopenharmony_ci if (attrlen > 0) { 543862306a36Sopenharmony_ci struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 543962306a36Sopenharmony_ci 544062306a36Sopenharmony_ci nla = nla_find(attrs, attrlen, RTA_GATEWAY); 544162306a36Sopenharmony_ci if (nla) { 544262306a36Sopenharmony_ci err = fib6_gw_from_attr(&r_cfg.fc_gateway, nla, 544362306a36Sopenharmony_ci extack); 544462306a36Sopenharmony_ci if (err) { 544562306a36Sopenharmony_ci last_err = err; 544662306a36Sopenharmony_ci goto next_rtnh; 544762306a36Sopenharmony_ci } 544862306a36Sopenharmony_ci 544962306a36Sopenharmony_ci r_cfg.fc_flags |= RTF_GATEWAY; 545062306a36Sopenharmony_ci } 545162306a36Sopenharmony_ci } 545262306a36Sopenharmony_ci err = ip6_route_del(&r_cfg, extack); 545362306a36Sopenharmony_ci if (err) 545462306a36Sopenharmony_ci last_err = err; 545562306a36Sopenharmony_ci 545662306a36Sopenharmony_cinext_rtnh: 545762306a36Sopenharmony_ci rtnh = rtnh_next(rtnh, &remaining); 545862306a36Sopenharmony_ci } 545962306a36Sopenharmony_ci 546062306a36Sopenharmony_ci return last_err; 546162306a36Sopenharmony_ci} 546262306a36Sopenharmony_ci 546362306a36Sopenharmony_cistatic int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 546462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 546562306a36Sopenharmony_ci{ 546662306a36Sopenharmony_ci struct fib6_config cfg; 546762306a36Sopenharmony_ci int err; 546862306a36Sopenharmony_ci 546962306a36Sopenharmony_ci err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 547062306a36Sopenharmony_ci if (err < 0) 547162306a36Sopenharmony_ci return err; 547262306a36Sopenharmony_ci 547362306a36Sopenharmony_ci if (cfg.fc_nh_id && 547462306a36Sopenharmony_ci !nexthop_find_by_id(sock_net(skb->sk), cfg.fc_nh_id)) { 547562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 547662306a36Sopenharmony_ci return -EINVAL; 547762306a36Sopenharmony_ci } 547862306a36Sopenharmony_ci 547962306a36Sopenharmony_ci if (cfg.fc_mp) 548062306a36Sopenharmony_ci return ip6_route_multipath_del(&cfg, extack); 548162306a36Sopenharmony_ci else { 548262306a36Sopenharmony_ci cfg.fc_delete_all_nh = 1; 548362306a36Sopenharmony_ci return ip6_route_del(&cfg, extack); 548462306a36Sopenharmony_ci } 548562306a36Sopenharmony_ci} 548662306a36Sopenharmony_ci 548762306a36Sopenharmony_cistatic int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 548862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 548962306a36Sopenharmony_ci{ 549062306a36Sopenharmony_ci struct fib6_config cfg; 549162306a36Sopenharmony_ci int err; 549262306a36Sopenharmony_ci 549362306a36Sopenharmony_ci err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 549462306a36Sopenharmony_ci if (err < 0) 549562306a36Sopenharmony_ci return err; 549662306a36Sopenharmony_ci 549762306a36Sopenharmony_ci if (cfg.fc_metric == 0) 549862306a36Sopenharmony_ci cfg.fc_metric = IP6_RT_PRIO_USER; 549962306a36Sopenharmony_ci 550062306a36Sopenharmony_ci if (cfg.fc_mp) 550162306a36Sopenharmony_ci return ip6_route_multipath_add(&cfg, extack); 550262306a36Sopenharmony_ci else 550362306a36Sopenharmony_ci return ip6_route_add(&cfg, GFP_KERNEL, extack); 550462306a36Sopenharmony_ci} 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_ci/* add the overhead of this fib6_nh to nexthop_len */ 550762306a36Sopenharmony_cistatic int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg) 550862306a36Sopenharmony_ci{ 550962306a36Sopenharmony_ci int *nexthop_len = arg; 551062306a36Sopenharmony_ci 551162306a36Sopenharmony_ci *nexthop_len += nla_total_size(0) /* RTA_MULTIPATH */ 551262306a36Sopenharmony_ci + NLA_ALIGN(sizeof(struct rtnexthop)) 551362306a36Sopenharmony_ci + nla_total_size(16); /* RTA_GATEWAY */ 551462306a36Sopenharmony_ci 551562306a36Sopenharmony_ci if (nh->fib_nh_lws) { 551662306a36Sopenharmony_ci /* RTA_ENCAP_TYPE */ 551762306a36Sopenharmony_ci *nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws); 551862306a36Sopenharmony_ci /* RTA_ENCAP */ 551962306a36Sopenharmony_ci *nexthop_len += nla_total_size(2); 552062306a36Sopenharmony_ci } 552162306a36Sopenharmony_ci 552262306a36Sopenharmony_ci return 0; 552362306a36Sopenharmony_ci} 552462306a36Sopenharmony_ci 552562306a36Sopenharmony_cistatic size_t rt6_nlmsg_size(struct fib6_info *f6i) 552662306a36Sopenharmony_ci{ 552762306a36Sopenharmony_ci int nexthop_len; 552862306a36Sopenharmony_ci 552962306a36Sopenharmony_ci if (f6i->nh) { 553062306a36Sopenharmony_ci nexthop_len = nla_total_size(4); /* RTA_NH_ID */ 553162306a36Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size, 553262306a36Sopenharmony_ci &nexthop_len); 553362306a36Sopenharmony_ci } else { 553462306a36Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 553562306a36Sopenharmony_ci struct fib6_nh *nh = f6i->fib6_nh; 553662306a36Sopenharmony_ci 553762306a36Sopenharmony_ci nexthop_len = 0; 553862306a36Sopenharmony_ci if (f6i->fib6_nsiblings) { 553962306a36Sopenharmony_ci rt6_nh_nlmsg_size(nh, &nexthop_len); 554062306a36Sopenharmony_ci 554162306a36Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 554262306a36Sopenharmony_ci &f6i->fib6_siblings, fib6_siblings) { 554362306a36Sopenharmony_ci rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len); 554462306a36Sopenharmony_ci } 554562306a36Sopenharmony_ci } 554662306a36Sopenharmony_ci nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws); 554762306a36Sopenharmony_ci } 554862306a36Sopenharmony_ci 554962306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct rtmsg)) 555062306a36Sopenharmony_ci + nla_total_size(16) /* RTA_SRC */ 555162306a36Sopenharmony_ci + nla_total_size(16) /* RTA_DST */ 555262306a36Sopenharmony_ci + nla_total_size(16) /* RTA_GATEWAY */ 555362306a36Sopenharmony_ci + nla_total_size(16) /* RTA_PREFSRC */ 555462306a36Sopenharmony_ci + nla_total_size(4) /* RTA_TABLE */ 555562306a36Sopenharmony_ci + nla_total_size(4) /* RTA_IIF */ 555662306a36Sopenharmony_ci + nla_total_size(4) /* RTA_OIF */ 555762306a36Sopenharmony_ci + nla_total_size(4) /* RTA_PRIORITY */ 555862306a36Sopenharmony_ci + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 555962306a36Sopenharmony_ci + nla_total_size(sizeof(struct rta_cacheinfo)) 556062306a36Sopenharmony_ci + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 556162306a36Sopenharmony_ci + nla_total_size(1) /* RTA_PREF */ 556262306a36Sopenharmony_ci + nexthop_len; 556362306a36Sopenharmony_ci} 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_cistatic int rt6_fill_node_nexthop(struct sk_buff *skb, struct nexthop *nh, 556662306a36Sopenharmony_ci unsigned char *flags) 556762306a36Sopenharmony_ci{ 556862306a36Sopenharmony_ci if (nexthop_is_multipath(nh)) { 556962306a36Sopenharmony_ci struct nlattr *mp; 557062306a36Sopenharmony_ci 557162306a36Sopenharmony_ci mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 557262306a36Sopenharmony_ci if (!mp) 557362306a36Sopenharmony_ci goto nla_put_failure; 557462306a36Sopenharmony_ci 557562306a36Sopenharmony_ci if (nexthop_mpath_fill_node(skb, nh, AF_INET6)) 557662306a36Sopenharmony_ci goto nla_put_failure; 557762306a36Sopenharmony_ci 557862306a36Sopenharmony_ci nla_nest_end(skb, mp); 557962306a36Sopenharmony_ci } else { 558062306a36Sopenharmony_ci struct fib6_nh *fib6_nh; 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_ci fib6_nh = nexthop_fib6_nh(nh); 558362306a36Sopenharmony_ci if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6, 558462306a36Sopenharmony_ci flags, false) < 0) 558562306a36Sopenharmony_ci goto nla_put_failure; 558662306a36Sopenharmony_ci } 558762306a36Sopenharmony_ci 558862306a36Sopenharmony_ci return 0; 558962306a36Sopenharmony_ci 559062306a36Sopenharmony_cinla_put_failure: 559162306a36Sopenharmony_ci return -EMSGSIZE; 559262306a36Sopenharmony_ci} 559362306a36Sopenharmony_ci 559462306a36Sopenharmony_cistatic int rt6_fill_node(struct net *net, struct sk_buff *skb, 559562306a36Sopenharmony_ci struct fib6_info *rt, struct dst_entry *dst, 559662306a36Sopenharmony_ci struct in6_addr *dest, struct in6_addr *src, 559762306a36Sopenharmony_ci int iif, int type, u32 portid, u32 seq, 559862306a36Sopenharmony_ci unsigned int flags) 559962306a36Sopenharmony_ci{ 560062306a36Sopenharmony_ci struct rt6_info *rt6 = (struct rt6_info *)dst; 560162306a36Sopenharmony_ci struct rt6key *rt6_dst, *rt6_src; 560262306a36Sopenharmony_ci u32 *pmetrics, table, rt6_flags; 560362306a36Sopenharmony_ci unsigned char nh_flags = 0; 560462306a36Sopenharmony_ci struct nlmsghdr *nlh; 560562306a36Sopenharmony_ci struct rtmsg *rtm; 560662306a36Sopenharmony_ci long expires = 0; 560762306a36Sopenharmony_ci 560862306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 560962306a36Sopenharmony_ci if (!nlh) 561062306a36Sopenharmony_ci return -EMSGSIZE; 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_ci if (rt6) { 561362306a36Sopenharmony_ci rt6_dst = &rt6->rt6i_dst; 561462306a36Sopenharmony_ci rt6_src = &rt6->rt6i_src; 561562306a36Sopenharmony_ci rt6_flags = rt6->rt6i_flags; 561662306a36Sopenharmony_ci } else { 561762306a36Sopenharmony_ci rt6_dst = &rt->fib6_dst; 561862306a36Sopenharmony_ci rt6_src = &rt->fib6_src; 561962306a36Sopenharmony_ci rt6_flags = rt->fib6_flags; 562062306a36Sopenharmony_ci } 562162306a36Sopenharmony_ci 562262306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 562362306a36Sopenharmony_ci rtm->rtm_family = AF_INET6; 562462306a36Sopenharmony_ci rtm->rtm_dst_len = rt6_dst->plen; 562562306a36Sopenharmony_ci rtm->rtm_src_len = rt6_src->plen; 562662306a36Sopenharmony_ci rtm->rtm_tos = 0; 562762306a36Sopenharmony_ci if (rt->fib6_table) 562862306a36Sopenharmony_ci table = rt->fib6_table->tb6_id; 562962306a36Sopenharmony_ci else 563062306a36Sopenharmony_ci table = RT6_TABLE_UNSPEC; 563162306a36Sopenharmony_ci rtm->rtm_table = table < 256 ? table : RT_TABLE_COMPAT; 563262306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_TABLE, table)) 563362306a36Sopenharmony_ci goto nla_put_failure; 563462306a36Sopenharmony_ci 563562306a36Sopenharmony_ci rtm->rtm_type = rt->fib6_type; 563662306a36Sopenharmony_ci rtm->rtm_flags = 0; 563762306a36Sopenharmony_ci rtm->rtm_scope = RT_SCOPE_UNIVERSE; 563862306a36Sopenharmony_ci rtm->rtm_protocol = rt->fib6_protocol; 563962306a36Sopenharmony_ci 564062306a36Sopenharmony_ci if (rt6_flags & RTF_CACHE) 564162306a36Sopenharmony_ci rtm->rtm_flags |= RTM_F_CLONED; 564262306a36Sopenharmony_ci 564362306a36Sopenharmony_ci if (dest) { 564462306a36Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_DST, dest)) 564562306a36Sopenharmony_ci goto nla_put_failure; 564662306a36Sopenharmony_ci rtm->rtm_dst_len = 128; 564762306a36Sopenharmony_ci } else if (rtm->rtm_dst_len) 564862306a36Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_DST, &rt6_dst->addr)) 564962306a36Sopenharmony_ci goto nla_put_failure; 565062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 565162306a36Sopenharmony_ci if (src) { 565262306a36Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_SRC, src)) 565362306a36Sopenharmony_ci goto nla_put_failure; 565462306a36Sopenharmony_ci rtm->rtm_src_len = 128; 565562306a36Sopenharmony_ci } else if (rtm->rtm_src_len && 565662306a36Sopenharmony_ci nla_put_in6_addr(skb, RTA_SRC, &rt6_src->addr)) 565762306a36Sopenharmony_ci goto nla_put_failure; 565862306a36Sopenharmony_ci#endif 565962306a36Sopenharmony_ci if (iif) { 566062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 566162306a36Sopenharmony_ci if (ipv6_addr_is_multicast(&rt6_dst->addr)) { 566262306a36Sopenharmony_ci int err = ip6mr_get_route(net, skb, rtm, portid); 566362306a36Sopenharmony_ci 566462306a36Sopenharmony_ci if (err == 0) 566562306a36Sopenharmony_ci return 0; 566662306a36Sopenharmony_ci if (err < 0) 566762306a36Sopenharmony_ci goto nla_put_failure; 566862306a36Sopenharmony_ci } else 566962306a36Sopenharmony_ci#endif 567062306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_IIF, iif)) 567162306a36Sopenharmony_ci goto nla_put_failure; 567262306a36Sopenharmony_ci } else if (dest) { 567362306a36Sopenharmony_ci struct in6_addr saddr_buf; 567462306a36Sopenharmony_ci if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 && 567562306a36Sopenharmony_ci nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 567662306a36Sopenharmony_ci goto nla_put_failure; 567762306a36Sopenharmony_ci } 567862306a36Sopenharmony_ci 567962306a36Sopenharmony_ci if (rt->fib6_prefsrc.plen) { 568062306a36Sopenharmony_ci struct in6_addr saddr_buf; 568162306a36Sopenharmony_ci saddr_buf = rt->fib6_prefsrc.addr; 568262306a36Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 568362306a36Sopenharmony_ci goto nla_put_failure; 568462306a36Sopenharmony_ci } 568562306a36Sopenharmony_ci 568662306a36Sopenharmony_ci pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics; 568762306a36Sopenharmony_ci if (rtnetlink_put_metrics(skb, pmetrics) < 0) 568862306a36Sopenharmony_ci goto nla_put_failure; 568962306a36Sopenharmony_ci 569062306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric)) 569162306a36Sopenharmony_ci goto nla_put_failure; 569262306a36Sopenharmony_ci 569362306a36Sopenharmony_ci /* For multipath routes, walk the siblings list and add 569462306a36Sopenharmony_ci * each as a nexthop within RTA_MULTIPATH. 569562306a36Sopenharmony_ci */ 569662306a36Sopenharmony_ci if (rt6) { 569762306a36Sopenharmony_ci if (rt6_flags & RTF_GATEWAY && 569862306a36Sopenharmony_ci nla_put_in6_addr(skb, RTA_GATEWAY, &rt6->rt6i_gateway)) 569962306a36Sopenharmony_ci goto nla_put_failure; 570062306a36Sopenharmony_ci 570162306a36Sopenharmony_ci if (dst->dev && nla_put_u32(skb, RTA_OIF, dst->dev->ifindex)) 570262306a36Sopenharmony_ci goto nla_put_failure; 570362306a36Sopenharmony_ci 570462306a36Sopenharmony_ci if (dst->lwtstate && 570562306a36Sopenharmony_ci lwtunnel_fill_encap(skb, dst->lwtstate, RTA_ENCAP, RTA_ENCAP_TYPE) < 0) 570662306a36Sopenharmony_ci goto nla_put_failure; 570762306a36Sopenharmony_ci } else if (rt->fib6_nsiblings) { 570862306a36Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 570962306a36Sopenharmony_ci struct nlattr *mp; 571062306a36Sopenharmony_ci 571162306a36Sopenharmony_ci mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 571262306a36Sopenharmony_ci if (!mp) 571362306a36Sopenharmony_ci goto nla_put_failure; 571462306a36Sopenharmony_ci 571562306a36Sopenharmony_ci if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common, 571662306a36Sopenharmony_ci rt->fib6_nh->fib_nh_weight, AF_INET6, 571762306a36Sopenharmony_ci 0) < 0) 571862306a36Sopenharmony_ci goto nla_put_failure; 571962306a36Sopenharmony_ci 572062306a36Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 572162306a36Sopenharmony_ci &rt->fib6_siblings, fib6_siblings) { 572262306a36Sopenharmony_ci if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common, 572362306a36Sopenharmony_ci sibling->fib6_nh->fib_nh_weight, 572462306a36Sopenharmony_ci AF_INET6, 0) < 0) 572562306a36Sopenharmony_ci goto nla_put_failure; 572662306a36Sopenharmony_ci } 572762306a36Sopenharmony_ci 572862306a36Sopenharmony_ci nla_nest_end(skb, mp); 572962306a36Sopenharmony_ci } else if (rt->nh) { 573062306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id)) 573162306a36Sopenharmony_ci goto nla_put_failure; 573262306a36Sopenharmony_ci 573362306a36Sopenharmony_ci if (nexthop_is_blackhole(rt->nh)) 573462306a36Sopenharmony_ci rtm->rtm_type = RTN_BLACKHOLE; 573562306a36Sopenharmony_ci 573662306a36Sopenharmony_ci if (READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode) && 573762306a36Sopenharmony_ci rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0) 573862306a36Sopenharmony_ci goto nla_put_failure; 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci rtm->rtm_flags |= nh_flags; 574162306a36Sopenharmony_ci } else { 574262306a36Sopenharmony_ci if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6, 574362306a36Sopenharmony_ci &nh_flags, false) < 0) 574462306a36Sopenharmony_ci goto nla_put_failure; 574562306a36Sopenharmony_ci 574662306a36Sopenharmony_ci rtm->rtm_flags |= nh_flags; 574762306a36Sopenharmony_ci } 574862306a36Sopenharmony_ci 574962306a36Sopenharmony_ci if (rt6_flags & RTF_EXPIRES) { 575062306a36Sopenharmony_ci expires = dst ? dst->expires : rt->expires; 575162306a36Sopenharmony_ci expires -= jiffies; 575262306a36Sopenharmony_ci } 575362306a36Sopenharmony_ci 575462306a36Sopenharmony_ci if (!dst) { 575562306a36Sopenharmony_ci if (READ_ONCE(rt->offload)) 575662306a36Sopenharmony_ci rtm->rtm_flags |= RTM_F_OFFLOAD; 575762306a36Sopenharmony_ci if (READ_ONCE(rt->trap)) 575862306a36Sopenharmony_ci rtm->rtm_flags |= RTM_F_TRAP; 575962306a36Sopenharmony_ci if (READ_ONCE(rt->offload_failed)) 576062306a36Sopenharmony_ci rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED; 576162306a36Sopenharmony_ci } 576262306a36Sopenharmony_ci 576362306a36Sopenharmony_ci if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0) 576462306a36Sopenharmony_ci goto nla_put_failure; 576562306a36Sopenharmony_ci 576662306a36Sopenharmony_ci if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags))) 576762306a36Sopenharmony_ci goto nla_put_failure; 576862306a36Sopenharmony_ci 576962306a36Sopenharmony_ci 577062306a36Sopenharmony_ci nlmsg_end(skb, nlh); 577162306a36Sopenharmony_ci return 0; 577262306a36Sopenharmony_ci 577362306a36Sopenharmony_cinla_put_failure: 577462306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 577562306a36Sopenharmony_ci return -EMSGSIZE; 577662306a36Sopenharmony_ci} 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_cistatic int fib6_info_nh_uses_dev(struct fib6_nh *nh, void *arg) 577962306a36Sopenharmony_ci{ 578062306a36Sopenharmony_ci const struct net_device *dev = arg; 578162306a36Sopenharmony_ci 578262306a36Sopenharmony_ci if (nh->fib_nh_dev == dev) 578362306a36Sopenharmony_ci return 1; 578462306a36Sopenharmony_ci 578562306a36Sopenharmony_ci return 0; 578662306a36Sopenharmony_ci} 578762306a36Sopenharmony_ci 578862306a36Sopenharmony_cistatic bool fib6_info_uses_dev(const struct fib6_info *f6i, 578962306a36Sopenharmony_ci const struct net_device *dev) 579062306a36Sopenharmony_ci{ 579162306a36Sopenharmony_ci if (f6i->nh) { 579262306a36Sopenharmony_ci struct net_device *_dev = (struct net_device *)dev; 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci return !!nexthop_for_each_fib6_nh(f6i->nh, 579562306a36Sopenharmony_ci fib6_info_nh_uses_dev, 579662306a36Sopenharmony_ci _dev); 579762306a36Sopenharmony_ci } 579862306a36Sopenharmony_ci 579962306a36Sopenharmony_ci if (f6i->fib6_nh->fib_nh_dev == dev) 580062306a36Sopenharmony_ci return true; 580162306a36Sopenharmony_ci 580262306a36Sopenharmony_ci if (f6i->fib6_nsiblings) { 580362306a36Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 580462306a36Sopenharmony_ci 580562306a36Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 580662306a36Sopenharmony_ci &f6i->fib6_siblings, fib6_siblings) { 580762306a36Sopenharmony_ci if (sibling->fib6_nh->fib_nh_dev == dev) 580862306a36Sopenharmony_ci return true; 580962306a36Sopenharmony_ci } 581062306a36Sopenharmony_ci } 581162306a36Sopenharmony_ci 581262306a36Sopenharmony_ci return false; 581362306a36Sopenharmony_ci} 581462306a36Sopenharmony_ci 581562306a36Sopenharmony_cistruct fib6_nh_exception_dump_walker { 581662306a36Sopenharmony_ci struct rt6_rtnl_dump_arg *dump; 581762306a36Sopenharmony_ci struct fib6_info *rt; 581862306a36Sopenharmony_ci unsigned int flags; 581962306a36Sopenharmony_ci unsigned int skip; 582062306a36Sopenharmony_ci unsigned int count; 582162306a36Sopenharmony_ci}; 582262306a36Sopenharmony_ci 582362306a36Sopenharmony_cistatic int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg) 582462306a36Sopenharmony_ci{ 582562306a36Sopenharmony_ci struct fib6_nh_exception_dump_walker *w = arg; 582662306a36Sopenharmony_ci struct rt6_rtnl_dump_arg *dump = w->dump; 582762306a36Sopenharmony_ci struct rt6_exception_bucket *bucket; 582862306a36Sopenharmony_ci struct rt6_exception *rt6_ex; 582962306a36Sopenharmony_ci int i, err; 583062306a36Sopenharmony_ci 583162306a36Sopenharmony_ci bucket = fib6_nh_get_excptn_bucket(nh, NULL); 583262306a36Sopenharmony_ci if (!bucket) 583362306a36Sopenharmony_ci return 0; 583462306a36Sopenharmony_ci 583562306a36Sopenharmony_ci for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 583662306a36Sopenharmony_ci hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 583762306a36Sopenharmony_ci if (w->skip) { 583862306a36Sopenharmony_ci w->skip--; 583962306a36Sopenharmony_ci continue; 584062306a36Sopenharmony_ci } 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci /* Expiration of entries doesn't bump sernum, insertion 584362306a36Sopenharmony_ci * does. Removal is triggered by insertion, so we can 584462306a36Sopenharmony_ci * rely on the fact that if entries change between two 584562306a36Sopenharmony_ci * partial dumps, this node is scanned again completely, 584662306a36Sopenharmony_ci * see rt6_insert_exception() and fib6_dump_table(). 584762306a36Sopenharmony_ci * 584862306a36Sopenharmony_ci * Count expired entries we go through as handled 584962306a36Sopenharmony_ci * entries that we'll skip next time, in case of partial 585062306a36Sopenharmony_ci * node dump. Otherwise, if entries expire meanwhile, 585162306a36Sopenharmony_ci * we'll skip the wrong amount. 585262306a36Sopenharmony_ci */ 585362306a36Sopenharmony_ci if (rt6_check_expired(rt6_ex->rt6i)) { 585462306a36Sopenharmony_ci w->count++; 585562306a36Sopenharmony_ci continue; 585662306a36Sopenharmony_ci } 585762306a36Sopenharmony_ci 585862306a36Sopenharmony_ci err = rt6_fill_node(dump->net, dump->skb, w->rt, 585962306a36Sopenharmony_ci &rt6_ex->rt6i->dst, NULL, NULL, 0, 586062306a36Sopenharmony_ci RTM_NEWROUTE, 586162306a36Sopenharmony_ci NETLINK_CB(dump->cb->skb).portid, 586262306a36Sopenharmony_ci dump->cb->nlh->nlmsg_seq, w->flags); 586362306a36Sopenharmony_ci if (err) 586462306a36Sopenharmony_ci return err; 586562306a36Sopenharmony_ci 586662306a36Sopenharmony_ci w->count++; 586762306a36Sopenharmony_ci } 586862306a36Sopenharmony_ci bucket++; 586962306a36Sopenharmony_ci } 587062306a36Sopenharmony_ci 587162306a36Sopenharmony_ci return 0; 587262306a36Sopenharmony_ci} 587362306a36Sopenharmony_ci 587462306a36Sopenharmony_ci/* Return -1 if done with node, number of handled routes on partial dump */ 587562306a36Sopenharmony_ciint rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip) 587662306a36Sopenharmony_ci{ 587762306a36Sopenharmony_ci struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 587862306a36Sopenharmony_ci struct fib_dump_filter *filter = &arg->filter; 587962306a36Sopenharmony_ci unsigned int flags = NLM_F_MULTI; 588062306a36Sopenharmony_ci struct net *net = arg->net; 588162306a36Sopenharmony_ci int count = 0; 588262306a36Sopenharmony_ci 588362306a36Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) 588462306a36Sopenharmony_ci return -1; 588562306a36Sopenharmony_ci 588662306a36Sopenharmony_ci if ((filter->flags & RTM_F_PREFIX) && 588762306a36Sopenharmony_ci !(rt->fib6_flags & RTF_PREFIX_RT)) { 588862306a36Sopenharmony_ci /* success since this is not a prefix route */ 588962306a36Sopenharmony_ci return -1; 589062306a36Sopenharmony_ci } 589162306a36Sopenharmony_ci if (filter->filter_set && 589262306a36Sopenharmony_ci ((filter->rt_type && rt->fib6_type != filter->rt_type) || 589362306a36Sopenharmony_ci (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) || 589462306a36Sopenharmony_ci (filter->protocol && rt->fib6_protocol != filter->protocol))) { 589562306a36Sopenharmony_ci return -1; 589662306a36Sopenharmony_ci } 589762306a36Sopenharmony_ci 589862306a36Sopenharmony_ci if (filter->filter_set || 589962306a36Sopenharmony_ci !filter->dump_routes || !filter->dump_exceptions) { 590062306a36Sopenharmony_ci flags |= NLM_F_DUMP_FILTERED; 590162306a36Sopenharmony_ci } 590262306a36Sopenharmony_ci 590362306a36Sopenharmony_ci if (filter->dump_routes) { 590462306a36Sopenharmony_ci if (skip) { 590562306a36Sopenharmony_ci skip--; 590662306a36Sopenharmony_ci } else { 590762306a36Sopenharmony_ci if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 590862306a36Sopenharmony_ci 0, RTM_NEWROUTE, 590962306a36Sopenharmony_ci NETLINK_CB(arg->cb->skb).portid, 591062306a36Sopenharmony_ci arg->cb->nlh->nlmsg_seq, flags)) { 591162306a36Sopenharmony_ci return 0; 591262306a36Sopenharmony_ci } 591362306a36Sopenharmony_ci count++; 591462306a36Sopenharmony_ci } 591562306a36Sopenharmony_ci } 591662306a36Sopenharmony_ci 591762306a36Sopenharmony_ci if (filter->dump_exceptions) { 591862306a36Sopenharmony_ci struct fib6_nh_exception_dump_walker w = { .dump = arg, 591962306a36Sopenharmony_ci .rt = rt, 592062306a36Sopenharmony_ci .flags = flags, 592162306a36Sopenharmony_ci .skip = skip, 592262306a36Sopenharmony_ci .count = 0 }; 592362306a36Sopenharmony_ci int err; 592462306a36Sopenharmony_ci 592562306a36Sopenharmony_ci rcu_read_lock(); 592662306a36Sopenharmony_ci if (rt->nh) { 592762306a36Sopenharmony_ci err = nexthop_for_each_fib6_nh(rt->nh, 592862306a36Sopenharmony_ci rt6_nh_dump_exceptions, 592962306a36Sopenharmony_ci &w); 593062306a36Sopenharmony_ci } else { 593162306a36Sopenharmony_ci err = rt6_nh_dump_exceptions(rt->fib6_nh, &w); 593262306a36Sopenharmony_ci } 593362306a36Sopenharmony_ci rcu_read_unlock(); 593462306a36Sopenharmony_ci 593562306a36Sopenharmony_ci if (err) 593662306a36Sopenharmony_ci return count + w.count; 593762306a36Sopenharmony_ci } 593862306a36Sopenharmony_ci 593962306a36Sopenharmony_ci return -1; 594062306a36Sopenharmony_ci} 594162306a36Sopenharmony_ci 594262306a36Sopenharmony_cistatic int inet6_rtm_valid_getroute_req(struct sk_buff *skb, 594362306a36Sopenharmony_ci const struct nlmsghdr *nlh, 594462306a36Sopenharmony_ci struct nlattr **tb, 594562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 594662306a36Sopenharmony_ci{ 594762306a36Sopenharmony_ci struct rtmsg *rtm; 594862306a36Sopenharmony_ci int i, err; 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 595162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 595262306a36Sopenharmony_ci "Invalid header for get route request"); 595362306a36Sopenharmony_ci return -EINVAL; 595462306a36Sopenharmony_ci } 595562306a36Sopenharmony_ci 595662306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 595762306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 595862306a36Sopenharmony_ci rtm_ipv6_policy, extack); 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 596162306a36Sopenharmony_ci if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) || 596262306a36Sopenharmony_ci (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) || 596362306a36Sopenharmony_ci rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope || 596462306a36Sopenharmony_ci rtm->rtm_type) { 596562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request"); 596662306a36Sopenharmony_ci return -EINVAL; 596762306a36Sopenharmony_ci } 596862306a36Sopenharmony_ci if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) { 596962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 597062306a36Sopenharmony_ci "Invalid flags for get route request"); 597162306a36Sopenharmony_ci return -EINVAL; 597262306a36Sopenharmony_ci } 597362306a36Sopenharmony_ci 597462306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 597562306a36Sopenharmony_ci rtm_ipv6_policy, extack); 597662306a36Sopenharmony_ci if (err) 597762306a36Sopenharmony_ci return err; 597862306a36Sopenharmony_ci 597962306a36Sopenharmony_ci if ((tb[RTA_SRC] && !rtm->rtm_src_len) || 598062306a36Sopenharmony_ci (tb[RTA_DST] && !rtm->rtm_dst_len)) { 598162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6"); 598262306a36Sopenharmony_ci return -EINVAL; 598362306a36Sopenharmony_ci } 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_ci for (i = 0; i <= RTA_MAX; i++) { 598662306a36Sopenharmony_ci if (!tb[i]) 598762306a36Sopenharmony_ci continue; 598862306a36Sopenharmony_ci 598962306a36Sopenharmony_ci switch (i) { 599062306a36Sopenharmony_ci case RTA_SRC: 599162306a36Sopenharmony_ci case RTA_DST: 599262306a36Sopenharmony_ci case RTA_IIF: 599362306a36Sopenharmony_ci case RTA_OIF: 599462306a36Sopenharmony_ci case RTA_MARK: 599562306a36Sopenharmony_ci case RTA_UID: 599662306a36Sopenharmony_ci case RTA_SPORT: 599762306a36Sopenharmony_ci case RTA_DPORT: 599862306a36Sopenharmony_ci case RTA_IP_PROTO: 599962306a36Sopenharmony_ci break; 600062306a36Sopenharmony_ci default: 600162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request"); 600262306a36Sopenharmony_ci return -EINVAL; 600362306a36Sopenharmony_ci } 600462306a36Sopenharmony_ci } 600562306a36Sopenharmony_ci 600662306a36Sopenharmony_ci return 0; 600762306a36Sopenharmony_ci} 600862306a36Sopenharmony_ci 600962306a36Sopenharmony_cistatic int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 601062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 601162306a36Sopenharmony_ci{ 601262306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 601362306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX+1]; 601462306a36Sopenharmony_ci int err, iif = 0, oif = 0; 601562306a36Sopenharmony_ci struct fib6_info *from; 601662306a36Sopenharmony_ci struct dst_entry *dst; 601762306a36Sopenharmony_ci struct rt6_info *rt; 601862306a36Sopenharmony_ci struct sk_buff *skb; 601962306a36Sopenharmony_ci struct rtmsg *rtm; 602062306a36Sopenharmony_ci struct flowi6 fl6 = {}; 602162306a36Sopenharmony_ci bool fibmatch; 602262306a36Sopenharmony_ci 602362306a36Sopenharmony_ci err = inet6_rtm_valid_getroute_req(in_skb, nlh, tb, extack); 602462306a36Sopenharmony_ci if (err < 0) 602562306a36Sopenharmony_ci goto errout; 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci err = -EINVAL; 602862306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 602962306a36Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 603062306a36Sopenharmony_ci fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH); 603162306a36Sopenharmony_ci 603262306a36Sopenharmony_ci if (tb[RTA_SRC]) { 603362306a36Sopenharmony_ci if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 603462306a36Sopenharmony_ci goto errout; 603562306a36Sopenharmony_ci 603662306a36Sopenharmony_ci fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 603762306a36Sopenharmony_ci } 603862306a36Sopenharmony_ci 603962306a36Sopenharmony_ci if (tb[RTA_DST]) { 604062306a36Sopenharmony_ci if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 604162306a36Sopenharmony_ci goto errout; 604262306a36Sopenharmony_ci 604362306a36Sopenharmony_ci fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 604462306a36Sopenharmony_ci } 604562306a36Sopenharmony_ci 604662306a36Sopenharmony_ci if (tb[RTA_IIF]) 604762306a36Sopenharmony_ci iif = nla_get_u32(tb[RTA_IIF]); 604862306a36Sopenharmony_ci 604962306a36Sopenharmony_ci if (tb[RTA_OIF]) 605062306a36Sopenharmony_ci oif = nla_get_u32(tb[RTA_OIF]); 605162306a36Sopenharmony_ci 605262306a36Sopenharmony_ci if (tb[RTA_MARK]) 605362306a36Sopenharmony_ci fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 605462306a36Sopenharmony_ci 605562306a36Sopenharmony_ci if (tb[RTA_UID]) 605662306a36Sopenharmony_ci fl6.flowi6_uid = make_kuid(current_user_ns(), 605762306a36Sopenharmony_ci nla_get_u32(tb[RTA_UID])); 605862306a36Sopenharmony_ci else 605962306a36Sopenharmony_ci fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_ci if (tb[RTA_SPORT]) 606262306a36Sopenharmony_ci fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]); 606362306a36Sopenharmony_ci 606462306a36Sopenharmony_ci if (tb[RTA_DPORT]) 606562306a36Sopenharmony_ci fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]); 606662306a36Sopenharmony_ci 606762306a36Sopenharmony_ci if (tb[RTA_IP_PROTO]) { 606862306a36Sopenharmony_ci err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO], 606962306a36Sopenharmony_ci &fl6.flowi6_proto, AF_INET6, 607062306a36Sopenharmony_ci extack); 607162306a36Sopenharmony_ci if (err) 607262306a36Sopenharmony_ci goto errout; 607362306a36Sopenharmony_ci } 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci if (iif) { 607662306a36Sopenharmony_ci struct net_device *dev; 607762306a36Sopenharmony_ci int flags = 0; 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci rcu_read_lock(); 608062306a36Sopenharmony_ci 608162306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, iif); 608262306a36Sopenharmony_ci if (!dev) { 608362306a36Sopenharmony_ci rcu_read_unlock(); 608462306a36Sopenharmony_ci err = -ENODEV; 608562306a36Sopenharmony_ci goto errout; 608662306a36Sopenharmony_ci } 608762306a36Sopenharmony_ci 608862306a36Sopenharmony_ci fl6.flowi6_iif = iif; 608962306a36Sopenharmony_ci 609062306a36Sopenharmony_ci if (!ipv6_addr_any(&fl6.saddr)) 609162306a36Sopenharmony_ci flags |= RT6_LOOKUP_F_HAS_SADDR; 609262306a36Sopenharmony_ci 609362306a36Sopenharmony_ci dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags); 609462306a36Sopenharmony_ci 609562306a36Sopenharmony_ci rcu_read_unlock(); 609662306a36Sopenharmony_ci } else { 609762306a36Sopenharmony_ci fl6.flowi6_oif = oif; 609862306a36Sopenharmony_ci 609962306a36Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 610062306a36Sopenharmony_ci } 610162306a36Sopenharmony_ci 610262306a36Sopenharmony_ci 610362306a36Sopenharmony_ci rt = container_of(dst, struct rt6_info, dst); 610462306a36Sopenharmony_ci if (rt->dst.error) { 610562306a36Sopenharmony_ci err = rt->dst.error; 610662306a36Sopenharmony_ci ip6_rt_put(rt); 610762306a36Sopenharmony_ci goto errout; 610862306a36Sopenharmony_ci } 610962306a36Sopenharmony_ci 611062306a36Sopenharmony_ci if (rt == net->ipv6.ip6_null_entry) { 611162306a36Sopenharmony_ci err = rt->dst.error; 611262306a36Sopenharmony_ci ip6_rt_put(rt); 611362306a36Sopenharmony_ci goto errout; 611462306a36Sopenharmony_ci } 611562306a36Sopenharmony_ci 611662306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 611762306a36Sopenharmony_ci if (!skb) { 611862306a36Sopenharmony_ci ip6_rt_put(rt); 611962306a36Sopenharmony_ci err = -ENOBUFS; 612062306a36Sopenharmony_ci goto errout; 612162306a36Sopenharmony_ci } 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci skb_dst_set(skb, &rt->dst); 612462306a36Sopenharmony_ci 612562306a36Sopenharmony_ci rcu_read_lock(); 612662306a36Sopenharmony_ci from = rcu_dereference(rt->from); 612762306a36Sopenharmony_ci if (from) { 612862306a36Sopenharmony_ci if (fibmatch) 612962306a36Sopenharmony_ci err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, 613062306a36Sopenharmony_ci iif, RTM_NEWROUTE, 613162306a36Sopenharmony_ci NETLINK_CB(in_skb).portid, 613262306a36Sopenharmony_ci nlh->nlmsg_seq, 0); 613362306a36Sopenharmony_ci else 613462306a36Sopenharmony_ci err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, 613562306a36Sopenharmony_ci &fl6.saddr, iif, RTM_NEWROUTE, 613662306a36Sopenharmony_ci NETLINK_CB(in_skb).portid, 613762306a36Sopenharmony_ci nlh->nlmsg_seq, 0); 613862306a36Sopenharmony_ci } else { 613962306a36Sopenharmony_ci err = -ENETUNREACH; 614062306a36Sopenharmony_ci } 614162306a36Sopenharmony_ci rcu_read_unlock(); 614262306a36Sopenharmony_ci 614362306a36Sopenharmony_ci if (err < 0) { 614462306a36Sopenharmony_ci kfree_skb(skb); 614562306a36Sopenharmony_ci goto errout; 614662306a36Sopenharmony_ci } 614762306a36Sopenharmony_ci 614862306a36Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 614962306a36Sopenharmony_cierrout: 615062306a36Sopenharmony_ci return err; 615162306a36Sopenharmony_ci} 615262306a36Sopenharmony_ci 615362306a36Sopenharmony_civoid inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info, 615462306a36Sopenharmony_ci unsigned int nlm_flags) 615562306a36Sopenharmony_ci{ 615662306a36Sopenharmony_ci struct sk_buff *skb; 615762306a36Sopenharmony_ci struct net *net = info->nl_net; 615862306a36Sopenharmony_ci u32 seq; 615962306a36Sopenharmony_ci int err; 616062306a36Sopenharmony_ci 616162306a36Sopenharmony_ci err = -ENOBUFS; 616262306a36Sopenharmony_ci seq = info->nlh ? info->nlh->nlmsg_seq : 0; 616362306a36Sopenharmony_ci 616462306a36Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 616562306a36Sopenharmony_ci if (!skb) 616662306a36Sopenharmony_ci goto errout; 616762306a36Sopenharmony_ci 616862306a36Sopenharmony_ci err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0, 616962306a36Sopenharmony_ci event, info->portid, seq, nlm_flags); 617062306a36Sopenharmony_ci if (err < 0) { 617162306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 617262306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 617362306a36Sopenharmony_ci kfree_skb(skb); 617462306a36Sopenharmony_ci goto errout; 617562306a36Sopenharmony_ci } 617662306a36Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 617762306a36Sopenharmony_ci info->nlh, gfp_any()); 617862306a36Sopenharmony_ci return; 617962306a36Sopenharmony_cierrout: 618062306a36Sopenharmony_ci if (err < 0) 618162306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 618262306a36Sopenharmony_ci} 618362306a36Sopenharmony_ci 618462306a36Sopenharmony_civoid fib6_rt_update(struct net *net, struct fib6_info *rt, 618562306a36Sopenharmony_ci struct nl_info *info) 618662306a36Sopenharmony_ci{ 618762306a36Sopenharmony_ci u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 618862306a36Sopenharmony_ci struct sk_buff *skb; 618962306a36Sopenharmony_ci int err = -ENOBUFS; 619062306a36Sopenharmony_ci 619162306a36Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 619262306a36Sopenharmony_ci if (!skb) 619362306a36Sopenharmony_ci goto errout; 619462306a36Sopenharmony_ci 619562306a36Sopenharmony_ci err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0, 619662306a36Sopenharmony_ci RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE); 619762306a36Sopenharmony_ci if (err < 0) { 619862306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 619962306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 620062306a36Sopenharmony_ci kfree_skb(skb); 620162306a36Sopenharmony_ci goto errout; 620262306a36Sopenharmony_ci } 620362306a36Sopenharmony_ci rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 620462306a36Sopenharmony_ci info->nlh, gfp_any()); 620562306a36Sopenharmony_ci return; 620662306a36Sopenharmony_cierrout: 620762306a36Sopenharmony_ci if (err < 0) 620862306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 620962306a36Sopenharmony_ci} 621062306a36Sopenharmony_ci 621162306a36Sopenharmony_civoid fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i, 621262306a36Sopenharmony_ci bool offload, bool trap, bool offload_failed) 621362306a36Sopenharmony_ci{ 621462306a36Sopenharmony_ci struct sk_buff *skb; 621562306a36Sopenharmony_ci int err; 621662306a36Sopenharmony_ci 621762306a36Sopenharmony_ci if (READ_ONCE(f6i->offload) == offload && 621862306a36Sopenharmony_ci READ_ONCE(f6i->trap) == trap && 621962306a36Sopenharmony_ci READ_ONCE(f6i->offload_failed) == offload_failed) 622062306a36Sopenharmony_ci return; 622162306a36Sopenharmony_ci 622262306a36Sopenharmony_ci WRITE_ONCE(f6i->offload, offload); 622362306a36Sopenharmony_ci WRITE_ONCE(f6i->trap, trap); 622462306a36Sopenharmony_ci 622562306a36Sopenharmony_ci /* 2 means send notifications only if offload_failed was changed. */ 622662306a36Sopenharmony_ci if (net->ipv6.sysctl.fib_notify_on_flag_change == 2 && 622762306a36Sopenharmony_ci READ_ONCE(f6i->offload_failed) == offload_failed) 622862306a36Sopenharmony_ci return; 622962306a36Sopenharmony_ci 623062306a36Sopenharmony_ci WRITE_ONCE(f6i->offload_failed, offload_failed); 623162306a36Sopenharmony_ci 623262306a36Sopenharmony_ci if (!rcu_access_pointer(f6i->fib6_node)) 623362306a36Sopenharmony_ci /* The route was removed from the tree, do not send 623462306a36Sopenharmony_ci * notification. 623562306a36Sopenharmony_ci */ 623662306a36Sopenharmony_ci return; 623762306a36Sopenharmony_ci 623862306a36Sopenharmony_ci if (!net->ipv6.sysctl.fib_notify_on_flag_change) 623962306a36Sopenharmony_ci return; 624062306a36Sopenharmony_ci 624162306a36Sopenharmony_ci skb = nlmsg_new(rt6_nlmsg_size(f6i), GFP_KERNEL); 624262306a36Sopenharmony_ci if (!skb) { 624362306a36Sopenharmony_ci err = -ENOBUFS; 624462306a36Sopenharmony_ci goto errout; 624562306a36Sopenharmony_ci } 624662306a36Sopenharmony_ci 624762306a36Sopenharmony_ci err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0, 624862306a36Sopenharmony_ci 0, 0); 624962306a36Sopenharmony_ci if (err < 0) { 625062306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 625162306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 625262306a36Sopenharmony_ci kfree_skb(skb); 625362306a36Sopenharmony_ci goto errout; 625462306a36Sopenharmony_ci } 625562306a36Sopenharmony_ci 625662306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_ROUTE, NULL, GFP_KERNEL); 625762306a36Sopenharmony_ci return; 625862306a36Sopenharmony_ci 625962306a36Sopenharmony_cierrout: 626062306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 626162306a36Sopenharmony_ci} 626262306a36Sopenharmony_ciEXPORT_SYMBOL(fib6_info_hw_flags_set); 626362306a36Sopenharmony_ci 626462306a36Sopenharmony_cistatic int ip6_route_dev_notify(struct notifier_block *this, 626562306a36Sopenharmony_ci unsigned long event, void *ptr) 626662306a36Sopenharmony_ci{ 626762306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 626862306a36Sopenharmony_ci struct net *net = dev_net(dev); 626962306a36Sopenharmony_ci 627062306a36Sopenharmony_ci if (!(dev->flags & IFF_LOOPBACK)) 627162306a36Sopenharmony_ci return NOTIFY_OK; 627262306a36Sopenharmony_ci 627362306a36Sopenharmony_ci if (event == NETDEV_REGISTER) { 627462306a36Sopenharmony_ci net->ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = dev; 627562306a36Sopenharmony_ci net->ipv6.ip6_null_entry->dst.dev = dev; 627662306a36Sopenharmony_ci net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 627762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 627862306a36Sopenharmony_ci net->ipv6.ip6_prohibit_entry->dst.dev = dev; 627962306a36Sopenharmony_ci net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 628062306a36Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 628162306a36Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 628262306a36Sopenharmony_ci#endif 628362306a36Sopenharmony_ci } else if (event == NETDEV_UNREGISTER && 628462306a36Sopenharmony_ci dev->reg_state != NETREG_UNREGISTERED) { 628562306a36Sopenharmony_ci /* NETDEV_UNREGISTER could be fired for multiple times by 628662306a36Sopenharmony_ci * netdev_wait_allrefs(). Make sure we only call this once. 628762306a36Sopenharmony_ci */ 628862306a36Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); 628962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 629062306a36Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); 629162306a36Sopenharmony_ci in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); 629262306a36Sopenharmony_ci#endif 629362306a36Sopenharmony_ci } 629462306a36Sopenharmony_ci 629562306a36Sopenharmony_ci return NOTIFY_OK; 629662306a36Sopenharmony_ci} 629762306a36Sopenharmony_ci 629862306a36Sopenharmony_ci/* 629962306a36Sopenharmony_ci * /proc 630062306a36Sopenharmony_ci */ 630162306a36Sopenharmony_ci 630262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 630362306a36Sopenharmony_cistatic int rt6_stats_seq_show(struct seq_file *seq, void *v) 630462306a36Sopenharmony_ci{ 630562306a36Sopenharmony_ci struct net *net = (struct net *)seq->private; 630662306a36Sopenharmony_ci seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 630762306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_nodes, 630862306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_route_nodes, 630962306a36Sopenharmony_ci atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc), 631062306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_entries, 631162306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_cache, 631262306a36Sopenharmony_ci dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 631362306a36Sopenharmony_ci net->ipv6.rt6_stats->fib_discarded_routes); 631462306a36Sopenharmony_ci 631562306a36Sopenharmony_ci return 0; 631662306a36Sopenharmony_ci} 631762306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 631862306a36Sopenharmony_ci 631962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 632062306a36Sopenharmony_ci 632162306a36Sopenharmony_cistatic int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 632262306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 632362306a36Sopenharmony_ci{ 632462306a36Sopenharmony_ci struct net *net; 632562306a36Sopenharmony_ci int delay; 632662306a36Sopenharmony_ci int ret; 632762306a36Sopenharmony_ci if (!write) 632862306a36Sopenharmony_ci return -EINVAL; 632962306a36Sopenharmony_ci 633062306a36Sopenharmony_ci net = (struct net *)ctl->extra1; 633162306a36Sopenharmony_ci delay = net->ipv6.sysctl.flush_delay; 633262306a36Sopenharmony_ci ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 633362306a36Sopenharmony_ci if (ret) 633462306a36Sopenharmony_ci return ret; 633562306a36Sopenharmony_ci 633662306a36Sopenharmony_ci fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 633762306a36Sopenharmony_ci return 0; 633862306a36Sopenharmony_ci} 633962306a36Sopenharmony_ci 634062306a36Sopenharmony_cistatic struct ctl_table ipv6_route_table_template[] = { 634162306a36Sopenharmony_ci { 634262306a36Sopenharmony_ci .procname = "max_size", 634362306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 634462306a36Sopenharmony_ci .maxlen = sizeof(int), 634562306a36Sopenharmony_ci .mode = 0644, 634662306a36Sopenharmony_ci .proc_handler = proc_dointvec, 634762306a36Sopenharmony_ci }, 634862306a36Sopenharmony_ci { 634962306a36Sopenharmony_ci .procname = "gc_thresh", 635062306a36Sopenharmony_ci .data = &ip6_dst_ops_template.gc_thresh, 635162306a36Sopenharmony_ci .maxlen = sizeof(int), 635262306a36Sopenharmony_ci .mode = 0644, 635362306a36Sopenharmony_ci .proc_handler = proc_dointvec, 635462306a36Sopenharmony_ci }, 635562306a36Sopenharmony_ci { 635662306a36Sopenharmony_ci .procname = "flush", 635762306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.flush_delay, 635862306a36Sopenharmony_ci .maxlen = sizeof(int), 635962306a36Sopenharmony_ci .mode = 0200, 636062306a36Sopenharmony_ci .proc_handler = ipv6_sysctl_rtcache_flush 636162306a36Sopenharmony_ci }, 636262306a36Sopenharmony_ci { 636362306a36Sopenharmony_ci .procname = "gc_min_interval", 636462306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 636562306a36Sopenharmony_ci .maxlen = sizeof(int), 636662306a36Sopenharmony_ci .mode = 0644, 636762306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 636862306a36Sopenharmony_ci }, 636962306a36Sopenharmony_ci { 637062306a36Sopenharmony_ci .procname = "gc_timeout", 637162306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 637262306a36Sopenharmony_ci .maxlen = sizeof(int), 637362306a36Sopenharmony_ci .mode = 0644, 637462306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 637562306a36Sopenharmony_ci }, 637662306a36Sopenharmony_ci { 637762306a36Sopenharmony_ci .procname = "gc_interval", 637862306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 637962306a36Sopenharmony_ci .maxlen = sizeof(int), 638062306a36Sopenharmony_ci .mode = 0644, 638162306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 638262306a36Sopenharmony_ci }, 638362306a36Sopenharmony_ci { 638462306a36Sopenharmony_ci .procname = "gc_elasticity", 638562306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 638662306a36Sopenharmony_ci .maxlen = sizeof(int), 638762306a36Sopenharmony_ci .mode = 0644, 638862306a36Sopenharmony_ci .proc_handler = proc_dointvec, 638962306a36Sopenharmony_ci }, 639062306a36Sopenharmony_ci { 639162306a36Sopenharmony_ci .procname = "mtu_expires", 639262306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 639362306a36Sopenharmony_ci .maxlen = sizeof(int), 639462306a36Sopenharmony_ci .mode = 0644, 639562306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 639662306a36Sopenharmony_ci }, 639762306a36Sopenharmony_ci { 639862306a36Sopenharmony_ci .procname = "min_adv_mss", 639962306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 640062306a36Sopenharmony_ci .maxlen = sizeof(int), 640162306a36Sopenharmony_ci .mode = 0644, 640262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 640362306a36Sopenharmony_ci }, 640462306a36Sopenharmony_ci { 640562306a36Sopenharmony_ci .procname = "gc_min_interval_ms", 640662306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 640762306a36Sopenharmony_ci .maxlen = sizeof(int), 640862306a36Sopenharmony_ci .mode = 0644, 640962306a36Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 641062306a36Sopenharmony_ci }, 641162306a36Sopenharmony_ci { 641262306a36Sopenharmony_ci .procname = "skip_notify_on_dev_down", 641362306a36Sopenharmony_ci .data = &init_net.ipv6.sysctl.skip_notify_on_dev_down, 641462306a36Sopenharmony_ci .maxlen = sizeof(u8), 641562306a36Sopenharmony_ci .mode = 0644, 641662306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 641762306a36Sopenharmony_ci .extra1 = SYSCTL_ZERO, 641862306a36Sopenharmony_ci .extra2 = SYSCTL_ONE, 641962306a36Sopenharmony_ci }, 642062306a36Sopenharmony_ci { } 642162306a36Sopenharmony_ci}; 642262306a36Sopenharmony_ci 642362306a36Sopenharmony_cistruct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 642462306a36Sopenharmony_ci{ 642562306a36Sopenharmony_ci struct ctl_table *table; 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_ci table = kmemdup(ipv6_route_table_template, 642862306a36Sopenharmony_ci sizeof(ipv6_route_table_template), 642962306a36Sopenharmony_ci GFP_KERNEL); 643062306a36Sopenharmony_ci 643162306a36Sopenharmony_ci if (table) { 643262306a36Sopenharmony_ci table[0].data = &net->ipv6.sysctl.ip6_rt_max_size; 643362306a36Sopenharmony_ci table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 643462306a36Sopenharmony_ci table[2].data = &net->ipv6.sysctl.flush_delay; 643562306a36Sopenharmony_ci table[2].extra1 = net; 643662306a36Sopenharmony_ci table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 643762306a36Sopenharmony_ci table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 643862306a36Sopenharmony_ci table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 643962306a36Sopenharmony_ci table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 644062306a36Sopenharmony_ci table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 644162306a36Sopenharmony_ci table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 644262306a36Sopenharmony_ci table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 644362306a36Sopenharmony_ci table[10].data = &net->ipv6.sysctl.skip_notify_on_dev_down; 644462306a36Sopenharmony_ci 644562306a36Sopenharmony_ci /* Don't export sysctls to unprivileged users */ 644662306a36Sopenharmony_ci if (net->user_ns != &init_user_ns) 644762306a36Sopenharmony_ci table[1].procname = NULL; 644862306a36Sopenharmony_ci } 644962306a36Sopenharmony_ci 645062306a36Sopenharmony_ci return table; 645162306a36Sopenharmony_ci} 645262306a36Sopenharmony_ci 645362306a36Sopenharmony_cisize_t ipv6_route_sysctl_table_size(struct net *net) 645462306a36Sopenharmony_ci{ 645562306a36Sopenharmony_ci /* Don't export sysctls to unprivileged users */ 645662306a36Sopenharmony_ci if (net->user_ns != &init_user_ns) 645762306a36Sopenharmony_ci return 1; 645862306a36Sopenharmony_ci 645962306a36Sopenharmony_ci return ARRAY_SIZE(ipv6_route_table_template); 646062306a36Sopenharmony_ci} 646162306a36Sopenharmony_ci#endif 646262306a36Sopenharmony_ci 646362306a36Sopenharmony_cistatic int __net_init ip6_route_net_init(struct net *net) 646462306a36Sopenharmony_ci{ 646562306a36Sopenharmony_ci int ret = -ENOMEM; 646662306a36Sopenharmony_ci 646762306a36Sopenharmony_ci memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 646862306a36Sopenharmony_ci sizeof(net->ipv6.ip6_dst_ops)); 646962306a36Sopenharmony_ci 647062306a36Sopenharmony_ci if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 647162306a36Sopenharmony_ci goto out_ip6_dst_ops; 647262306a36Sopenharmony_ci 647362306a36Sopenharmony_ci net->ipv6.fib6_null_entry = fib6_info_alloc(GFP_KERNEL, true); 647462306a36Sopenharmony_ci if (!net->ipv6.fib6_null_entry) 647562306a36Sopenharmony_ci goto out_ip6_dst_entries; 647662306a36Sopenharmony_ci memcpy(net->ipv6.fib6_null_entry, &fib6_null_entry_template, 647762306a36Sopenharmony_ci sizeof(*net->ipv6.fib6_null_entry)); 647862306a36Sopenharmony_ci 647962306a36Sopenharmony_ci net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 648062306a36Sopenharmony_ci sizeof(*net->ipv6.ip6_null_entry), 648162306a36Sopenharmony_ci GFP_KERNEL); 648262306a36Sopenharmony_ci if (!net->ipv6.ip6_null_entry) 648362306a36Sopenharmony_ci goto out_fib6_null_entry; 648462306a36Sopenharmony_ci net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 648562306a36Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 648662306a36Sopenharmony_ci ip6_template_metrics, true); 648762306a36Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->dst.rt_uncached); 648862306a36Sopenharmony_ci 648962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 649062306a36Sopenharmony_ci net->ipv6.fib6_has_custom_rules = false; 649162306a36Sopenharmony_ci net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 649262306a36Sopenharmony_ci sizeof(*net->ipv6.ip6_prohibit_entry), 649362306a36Sopenharmony_ci GFP_KERNEL); 649462306a36Sopenharmony_ci if (!net->ipv6.ip6_prohibit_entry) 649562306a36Sopenharmony_ci goto out_ip6_null_entry; 649662306a36Sopenharmony_ci net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 649762306a36Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 649862306a36Sopenharmony_ci ip6_template_metrics, true); 649962306a36Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->dst.rt_uncached); 650062306a36Sopenharmony_ci 650162306a36Sopenharmony_ci net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 650262306a36Sopenharmony_ci sizeof(*net->ipv6.ip6_blk_hole_entry), 650362306a36Sopenharmony_ci GFP_KERNEL); 650462306a36Sopenharmony_ci if (!net->ipv6.ip6_blk_hole_entry) 650562306a36Sopenharmony_ci goto out_ip6_prohibit_entry; 650662306a36Sopenharmony_ci net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 650762306a36Sopenharmony_ci dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 650862306a36Sopenharmony_ci ip6_template_metrics, true); 650962306a36Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->dst.rt_uncached); 651062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 651162306a36Sopenharmony_ci net->ipv6.fib6_routes_require_src = 0; 651262306a36Sopenharmony_ci#endif 651362306a36Sopenharmony_ci#endif 651462306a36Sopenharmony_ci 651562306a36Sopenharmony_ci net->ipv6.sysctl.flush_delay = 0; 651662306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_max_size = INT_MAX; 651762306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 651862306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 651962306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 652062306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 652162306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 652262306a36Sopenharmony_ci net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 652362306a36Sopenharmony_ci net->ipv6.sysctl.skip_notify_on_dev_down = 0; 652462306a36Sopenharmony_ci 652562306a36Sopenharmony_ci atomic_set(&net->ipv6.ip6_rt_gc_expire, 30*HZ); 652662306a36Sopenharmony_ci 652762306a36Sopenharmony_ci ret = 0; 652862306a36Sopenharmony_ciout: 652962306a36Sopenharmony_ci return ret; 653062306a36Sopenharmony_ci 653162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 653262306a36Sopenharmony_ciout_ip6_prohibit_entry: 653362306a36Sopenharmony_ci kfree(net->ipv6.ip6_prohibit_entry); 653462306a36Sopenharmony_ciout_ip6_null_entry: 653562306a36Sopenharmony_ci kfree(net->ipv6.ip6_null_entry); 653662306a36Sopenharmony_ci#endif 653762306a36Sopenharmony_ciout_fib6_null_entry: 653862306a36Sopenharmony_ci kfree(net->ipv6.fib6_null_entry); 653962306a36Sopenharmony_ciout_ip6_dst_entries: 654062306a36Sopenharmony_ci dst_entries_destroy(&net->ipv6.ip6_dst_ops); 654162306a36Sopenharmony_ciout_ip6_dst_ops: 654262306a36Sopenharmony_ci goto out; 654362306a36Sopenharmony_ci} 654462306a36Sopenharmony_ci 654562306a36Sopenharmony_cistatic void __net_exit ip6_route_net_exit(struct net *net) 654662306a36Sopenharmony_ci{ 654762306a36Sopenharmony_ci kfree(net->ipv6.fib6_null_entry); 654862306a36Sopenharmony_ci kfree(net->ipv6.ip6_null_entry); 654962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 655062306a36Sopenharmony_ci kfree(net->ipv6.ip6_prohibit_entry); 655162306a36Sopenharmony_ci kfree(net->ipv6.ip6_blk_hole_entry); 655262306a36Sopenharmony_ci#endif 655362306a36Sopenharmony_ci dst_entries_destroy(&net->ipv6.ip6_dst_ops); 655462306a36Sopenharmony_ci} 655562306a36Sopenharmony_ci 655662306a36Sopenharmony_cistatic int __net_init ip6_route_net_init_late(struct net *net) 655762306a36Sopenharmony_ci{ 655862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 655962306a36Sopenharmony_ci if (!proc_create_net("ipv6_route", 0, net->proc_net, 656062306a36Sopenharmony_ci &ipv6_route_seq_ops, 656162306a36Sopenharmony_ci sizeof(struct ipv6_route_iter))) 656262306a36Sopenharmony_ci return -ENOMEM; 656362306a36Sopenharmony_ci 656462306a36Sopenharmony_ci if (!proc_create_net_single("rt6_stats", 0444, net->proc_net, 656562306a36Sopenharmony_ci rt6_stats_seq_show, NULL)) { 656662306a36Sopenharmony_ci remove_proc_entry("ipv6_route", net->proc_net); 656762306a36Sopenharmony_ci return -ENOMEM; 656862306a36Sopenharmony_ci } 656962306a36Sopenharmony_ci#endif 657062306a36Sopenharmony_ci return 0; 657162306a36Sopenharmony_ci} 657262306a36Sopenharmony_ci 657362306a36Sopenharmony_cistatic void __net_exit ip6_route_net_exit_late(struct net *net) 657462306a36Sopenharmony_ci{ 657562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 657662306a36Sopenharmony_ci remove_proc_entry("ipv6_route", net->proc_net); 657762306a36Sopenharmony_ci remove_proc_entry("rt6_stats", net->proc_net); 657862306a36Sopenharmony_ci#endif 657962306a36Sopenharmony_ci} 658062306a36Sopenharmony_ci 658162306a36Sopenharmony_cistatic struct pernet_operations ip6_route_net_ops = { 658262306a36Sopenharmony_ci .init = ip6_route_net_init, 658362306a36Sopenharmony_ci .exit = ip6_route_net_exit, 658462306a36Sopenharmony_ci}; 658562306a36Sopenharmony_ci 658662306a36Sopenharmony_cistatic int __net_init ipv6_inetpeer_init(struct net *net) 658762306a36Sopenharmony_ci{ 658862306a36Sopenharmony_ci struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 658962306a36Sopenharmony_ci 659062306a36Sopenharmony_ci if (!bp) 659162306a36Sopenharmony_ci return -ENOMEM; 659262306a36Sopenharmony_ci inet_peer_base_init(bp); 659362306a36Sopenharmony_ci net->ipv6.peers = bp; 659462306a36Sopenharmony_ci return 0; 659562306a36Sopenharmony_ci} 659662306a36Sopenharmony_ci 659762306a36Sopenharmony_cistatic void __net_exit ipv6_inetpeer_exit(struct net *net) 659862306a36Sopenharmony_ci{ 659962306a36Sopenharmony_ci struct inet_peer_base *bp = net->ipv6.peers; 660062306a36Sopenharmony_ci 660162306a36Sopenharmony_ci net->ipv6.peers = NULL; 660262306a36Sopenharmony_ci inetpeer_invalidate_tree(bp); 660362306a36Sopenharmony_ci kfree(bp); 660462306a36Sopenharmony_ci} 660562306a36Sopenharmony_ci 660662306a36Sopenharmony_cistatic struct pernet_operations ipv6_inetpeer_ops = { 660762306a36Sopenharmony_ci .init = ipv6_inetpeer_init, 660862306a36Sopenharmony_ci .exit = ipv6_inetpeer_exit, 660962306a36Sopenharmony_ci}; 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_cistatic struct pernet_operations ip6_route_net_late_ops = { 661262306a36Sopenharmony_ci .init = ip6_route_net_init_late, 661362306a36Sopenharmony_ci .exit = ip6_route_net_exit_late, 661462306a36Sopenharmony_ci}; 661562306a36Sopenharmony_ci 661662306a36Sopenharmony_cistatic struct notifier_block ip6_route_dev_notifier = { 661762306a36Sopenharmony_ci .notifier_call = ip6_route_dev_notify, 661862306a36Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY - 10, 661962306a36Sopenharmony_ci}; 662062306a36Sopenharmony_ci 662162306a36Sopenharmony_civoid __init ip6_route_init_special_entries(void) 662262306a36Sopenharmony_ci{ 662362306a36Sopenharmony_ci /* Registering of the loopback is done before this portion of code, 662462306a36Sopenharmony_ci * the loopback reference in rt6_info will not be taken, do it 662562306a36Sopenharmony_ci * manually for init_net */ 662662306a36Sopenharmony_ci init_net.ipv6.fib6_null_entry->fib6_nh->fib_nh_dev = init_net.loopback_dev; 662762306a36Sopenharmony_ci init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 662862306a36Sopenharmony_ci init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 662962306a36Sopenharmony_ci #ifdef CONFIG_IPV6_MULTIPLE_TABLES 663062306a36Sopenharmony_ci init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 663162306a36Sopenharmony_ci init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 663262306a36Sopenharmony_ci init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 663362306a36Sopenharmony_ci init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 663462306a36Sopenharmony_ci #endif 663562306a36Sopenharmony_ci} 663662306a36Sopenharmony_ci 663762306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 663862306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 663962306a36Sopenharmony_ciDEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt) 664062306a36Sopenharmony_ci 664162306a36Sopenharmony_ciBTF_ID_LIST(btf_fib6_info_id) 664262306a36Sopenharmony_ciBTF_ID(struct, fib6_info) 664362306a36Sopenharmony_ci 664462306a36Sopenharmony_cistatic const struct bpf_iter_seq_info ipv6_route_seq_info = { 664562306a36Sopenharmony_ci .seq_ops = &ipv6_route_seq_ops, 664662306a36Sopenharmony_ci .init_seq_private = bpf_iter_init_seq_net, 664762306a36Sopenharmony_ci .fini_seq_private = bpf_iter_fini_seq_net, 664862306a36Sopenharmony_ci .seq_priv_size = sizeof(struct ipv6_route_iter), 664962306a36Sopenharmony_ci}; 665062306a36Sopenharmony_ci 665162306a36Sopenharmony_cistatic struct bpf_iter_reg ipv6_route_reg_info = { 665262306a36Sopenharmony_ci .target = "ipv6_route", 665362306a36Sopenharmony_ci .ctx_arg_info_size = 1, 665462306a36Sopenharmony_ci .ctx_arg_info = { 665562306a36Sopenharmony_ci { offsetof(struct bpf_iter__ipv6_route, rt), 665662306a36Sopenharmony_ci PTR_TO_BTF_ID_OR_NULL }, 665762306a36Sopenharmony_ci }, 665862306a36Sopenharmony_ci .seq_info = &ipv6_route_seq_info, 665962306a36Sopenharmony_ci}; 666062306a36Sopenharmony_ci 666162306a36Sopenharmony_cistatic int __init bpf_iter_register(void) 666262306a36Sopenharmony_ci{ 666362306a36Sopenharmony_ci ipv6_route_reg_info.ctx_arg_info[0].btf_id = *btf_fib6_info_id; 666462306a36Sopenharmony_ci return bpf_iter_reg_target(&ipv6_route_reg_info); 666562306a36Sopenharmony_ci} 666662306a36Sopenharmony_ci 666762306a36Sopenharmony_cistatic void bpf_iter_unregister(void) 666862306a36Sopenharmony_ci{ 666962306a36Sopenharmony_ci bpf_iter_unreg_target(&ipv6_route_reg_info); 667062306a36Sopenharmony_ci} 667162306a36Sopenharmony_ci#endif 667262306a36Sopenharmony_ci#endif 667362306a36Sopenharmony_ci 667462306a36Sopenharmony_ciint __init ip6_route_init(void) 667562306a36Sopenharmony_ci{ 667662306a36Sopenharmony_ci int ret; 667762306a36Sopenharmony_ci int cpu; 667862306a36Sopenharmony_ci 667962306a36Sopenharmony_ci ret = -ENOMEM; 668062306a36Sopenharmony_ci ip6_dst_ops_template.kmem_cachep = 668162306a36Sopenharmony_ci kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 668262306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL); 668362306a36Sopenharmony_ci if (!ip6_dst_ops_template.kmem_cachep) 668462306a36Sopenharmony_ci goto out; 668562306a36Sopenharmony_ci 668662306a36Sopenharmony_ci ret = dst_entries_init(&ip6_dst_blackhole_ops); 668762306a36Sopenharmony_ci if (ret) 668862306a36Sopenharmony_ci goto out_kmem_cache; 668962306a36Sopenharmony_ci 669062306a36Sopenharmony_ci ret = register_pernet_subsys(&ipv6_inetpeer_ops); 669162306a36Sopenharmony_ci if (ret) 669262306a36Sopenharmony_ci goto out_dst_entries; 669362306a36Sopenharmony_ci 669462306a36Sopenharmony_ci ret = register_pernet_subsys(&ip6_route_net_ops); 669562306a36Sopenharmony_ci if (ret) 669662306a36Sopenharmony_ci goto out_register_inetpeer; 669762306a36Sopenharmony_ci 669862306a36Sopenharmony_ci ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 669962306a36Sopenharmony_ci 670062306a36Sopenharmony_ci ret = fib6_init(); 670162306a36Sopenharmony_ci if (ret) 670262306a36Sopenharmony_ci goto out_register_subsys; 670362306a36Sopenharmony_ci 670462306a36Sopenharmony_ci ret = xfrm6_init(); 670562306a36Sopenharmony_ci if (ret) 670662306a36Sopenharmony_ci goto out_fib6_init; 670762306a36Sopenharmony_ci 670862306a36Sopenharmony_ci ret = fib6_rules_init(); 670962306a36Sopenharmony_ci if (ret) 671062306a36Sopenharmony_ci goto xfrm6_init; 671162306a36Sopenharmony_ci 671262306a36Sopenharmony_ci ret = register_pernet_subsys(&ip6_route_net_late_ops); 671362306a36Sopenharmony_ci if (ret) 671462306a36Sopenharmony_ci goto fib6_rules_init; 671562306a36Sopenharmony_ci 671662306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE, 671762306a36Sopenharmony_ci inet6_rtm_newroute, NULL, 0); 671862306a36Sopenharmony_ci if (ret < 0) 671962306a36Sopenharmony_ci goto out_register_late_subsys; 672062306a36Sopenharmony_ci 672162306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE, 672262306a36Sopenharmony_ci inet6_rtm_delroute, NULL, 0); 672362306a36Sopenharmony_ci if (ret < 0) 672462306a36Sopenharmony_ci goto out_register_late_subsys; 672562306a36Sopenharmony_ci 672662306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, 672762306a36Sopenharmony_ci inet6_rtm_getroute, NULL, 672862306a36Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 672962306a36Sopenharmony_ci if (ret < 0) 673062306a36Sopenharmony_ci goto out_register_late_subsys; 673162306a36Sopenharmony_ci 673262306a36Sopenharmony_ci ret = register_netdevice_notifier(&ip6_route_dev_notifier); 673362306a36Sopenharmony_ci if (ret) 673462306a36Sopenharmony_ci goto out_register_late_subsys; 673562306a36Sopenharmony_ci 673662306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 673762306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 673862306a36Sopenharmony_ci ret = bpf_iter_register(); 673962306a36Sopenharmony_ci if (ret) 674062306a36Sopenharmony_ci goto out_register_late_subsys; 674162306a36Sopenharmony_ci#endif 674262306a36Sopenharmony_ci#endif 674362306a36Sopenharmony_ci 674462306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 674562306a36Sopenharmony_ci struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 674662306a36Sopenharmony_ci 674762306a36Sopenharmony_ci INIT_LIST_HEAD(&ul->head); 674862306a36Sopenharmony_ci INIT_LIST_HEAD(&ul->quarantine); 674962306a36Sopenharmony_ci spin_lock_init(&ul->lock); 675062306a36Sopenharmony_ci } 675162306a36Sopenharmony_ci 675262306a36Sopenharmony_ciout: 675362306a36Sopenharmony_ci return ret; 675462306a36Sopenharmony_ci 675562306a36Sopenharmony_ciout_register_late_subsys: 675662306a36Sopenharmony_ci rtnl_unregister_all(PF_INET6); 675762306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_late_ops); 675862306a36Sopenharmony_cifib6_rules_init: 675962306a36Sopenharmony_ci fib6_rules_cleanup(); 676062306a36Sopenharmony_cixfrm6_init: 676162306a36Sopenharmony_ci xfrm6_fini(); 676262306a36Sopenharmony_ciout_fib6_init: 676362306a36Sopenharmony_ci fib6_gc_cleanup(); 676462306a36Sopenharmony_ciout_register_subsys: 676562306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_ops); 676662306a36Sopenharmony_ciout_register_inetpeer: 676762306a36Sopenharmony_ci unregister_pernet_subsys(&ipv6_inetpeer_ops); 676862306a36Sopenharmony_ciout_dst_entries: 676962306a36Sopenharmony_ci dst_entries_destroy(&ip6_dst_blackhole_ops); 677062306a36Sopenharmony_ciout_kmem_cache: 677162306a36Sopenharmony_ci kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 677262306a36Sopenharmony_ci goto out; 677362306a36Sopenharmony_ci} 677462306a36Sopenharmony_ci 677562306a36Sopenharmony_civoid ip6_route_cleanup(void) 677662306a36Sopenharmony_ci{ 677762306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) 677862306a36Sopenharmony_ci#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 677962306a36Sopenharmony_ci bpf_iter_unregister(); 678062306a36Sopenharmony_ci#endif 678162306a36Sopenharmony_ci#endif 678262306a36Sopenharmony_ci unregister_netdevice_notifier(&ip6_route_dev_notifier); 678362306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_late_ops); 678462306a36Sopenharmony_ci fib6_rules_cleanup(); 678562306a36Sopenharmony_ci xfrm6_fini(); 678662306a36Sopenharmony_ci fib6_gc_cleanup(); 678762306a36Sopenharmony_ci unregister_pernet_subsys(&ipv6_inetpeer_ops); 678862306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_route_net_ops); 678962306a36Sopenharmony_ci dst_entries_destroy(&ip6_dst_blackhole_ops); 679062306a36Sopenharmony_ci kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 679162306a36Sopenharmony_ci} 6792