18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux INET6 implementation 48c2ecf20Sopenharmony_ci * Forwarding Information Database 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Changes: 108c2ecf20Sopenharmony_ci * Yuji SEKIYA @USAGI: Support default route on router node; 118c2ecf20Sopenharmony_ci * remove ip6_null_entry from the top of 128c2ecf20Sopenharmony_ci * routing table. 138c2ecf20Sopenharmony_ci * Ville Nuorvala: Fixed routing subtrees. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/net.h> 218c2ecf20Sopenharmony_ci#include <linux/route.h> 228c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/in6.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/list.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <net/ip.h> 298c2ecf20Sopenharmony_ci#include <net/ipv6.h> 308c2ecf20Sopenharmony_ci#include <net/ndisc.h> 318c2ecf20Sopenharmony_ci#include <net/addrconf.h> 328c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 338c2ecf20Sopenharmony_ci#include <net/fib_notifier.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <net/ip6_fib.h> 368c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct kmem_cache *fib6_node_kmem __read_mostly; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct fib6_cleaner { 418c2ecf20Sopenharmony_ci struct fib6_walker w; 428c2ecf20Sopenharmony_ci struct net *net; 438c2ecf20Sopenharmony_ci int (*func)(struct fib6_info *, void *arg); 448c2ecf20Sopenharmony_ci int sernum; 458c2ecf20Sopenharmony_ci void *arg; 468c2ecf20Sopenharmony_ci bool skip_notify; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 508c2ecf20Sopenharmony_ci#define FWS_INIT FWS_S 518c2ecf20Sopenharmony_ci#else 528c2ecf20Sopenharmony_ci#define FWS_INIT FWS_L 538c2ecf20Sopenharmony_ci#endif 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net, 568c2ecf20Sopenharmony_ci struct fib6_table *table, 578c2ecf20Sopenharmony_ci struct fib6_node *fn); 588c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net, 598c2ecf20Sopenharmony_ci struct fib6_table *table, 608c2ecf20Sopenharmony_ci struct fib6_node *fn); 618c2ecf20Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w); 628c2ecf20Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * A routing update causes an increase of the serial number on the 668c2ecf20Sopenharmony_ci * affected subtree. This allows for cached routes to be asynchronously 678c2ecf20Sopenharmony_ci * tested when modifications are made to the destination cache as a 688c2ecf20Sopenharmony_ci * result of redirects, path MTU changes, etc. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define FOR_WALKERS(net, w) \ 748c2ecf20Sopenharmony_ci list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void fib6_walker_link(struct net *net, struct fib6_walker *w) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci write_lock_bh(&net->ipv6.fib6_walker_lock); 798c2ecf20Sopenharmony_ci list_add(&w->lh, &net->ipv6.fib6_walkers); 808c2ecf20Sopenharmony_ci write_unlock_bh(&net->ipv6.fib6_walker_lock); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void fib6_walker_unlink(struct net *net, struct fib6_walker *w) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci write_lock_bh(&net->ipv6.fib6_walker_lock); 868c2ecf20Sopenharmony_ci list_del(&w->lh); 878c2ecf20Sopenharmony_ci write_unlock_bh(&net->ipv6.fib6_walker_lock); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int fib6_new_sernum(struct net *net) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int new, old; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci do { 958c2ecf20Sopenharmony_ci old = atomic_read(&net->ipv6.fib6_sernum); 968c2ecf20Sopenharmony_ci new = old < INT_MAX ? old + 1 : 1; 978c2ecf20Sopenharmony_ci } while (atomic_cmpxchg(&net->ipv6.fib6_sernum, 988c2ecf20Sopenharmony_ci old, new) != old); 998c2ecf20Sopenharmony_ci return new; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cienum { 1038c2ecf20Sopenharmony_ci FIB6_NO_SERNUM_CHANGE = 0, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_civoid fib6_update_sernum(struct net *net, struct fib6_info *f6i) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct fib6_node *fn; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(f6i->fib6_node, 1118c2ecf20Sopenharmony_ci lockdep_is_held(&f6i->fib6_table->tb6_lock)); 1128c2ecf20Sopenharmony_ci if (fn) 1138c2ecf20Sopenharmony_ci WRITE_ONCE(fn->fn_sernum, fib6_new_sernum(net)); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Auxiliary address test functions for the radix tree. 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * These assume a 32bit processor (although it will work on 1208c2ecf20Sopenharmony_ci * 64bit processors) 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * test bit 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN) 1278c2ecf20Sopenharmony_ci# define BITOP_BE32_SWIZZLE (0x1F & ~7) 1288c2ecf20Sopenharmony_ci#else 1298c2ecf20Sopenharmony_ci# define BITOP_BE32_SWIZZLE 0 1308c2ecf20Sopenharmony_ci#endif 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic __be32 addr_bit_set(const void *token, int fn_bit) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci const __be32 *addr = token; 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * Here, 1378c2ecf20Sopenharmony_ci * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f) 1388c2ecf20Sopenharmony_ci * is optimized version of 1398c2ecf20Sopenharmony_ci * htonl(1 << ((~fn_bit)&0x1F)) 1408c2ecf20Sopenharmony_ci * See include/asm-generic/bitops/le.h. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) & 1438c2ecf20Sopenharmony_ci addr[fn_bit >> 5]; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct fib6_info *f6i; 1498c2ecf20Sopenharmony_ci size_t sz = sizeof(*f6i); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (with_fib6_nh) 1528c2ecf20Sopenharmony_ci sz += sizeof(struct fib6_nh); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci f6i = kzalloc(sz, gfp_flags); 1558c2ecf20Sopenharmony_ci if (!f6i) 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* fib6_siblings is a union with nh_list, so this initializes both */ 1598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&f6i->fib6_siblings); 1608c2ecf20Sopenharmony_ci refcount_set(&f6i->fib6_ref, 1); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return f6i; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_civoid fib6_info_destroy_rcu(struct rcu_head *head) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct fib6_info *f6i = container_of(head, struct fib6_info, rcu); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci WARN_ON(f6i->fib6_node); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (f6i->nh) 1728c2ecf20Sopenharmony_ci nexthop_put(f6i->nh); 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci fib6_nh_release(f6i->fib6_nh); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ip_fib_metrics_put(f6i->fib6_metrics); 1778c2ecf20Sopenharmony_ci kfree(f6i); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_info_destroy_rcu); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic struct fib6_node *node_alloc(struct net *net) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct fib6_node *fn; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC); 1868c2ecf20Sopenharmony_ci if (fn) 1878c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_nodes++; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return fn; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void node_free_immediate(struct net *net, struct fib6_node *fn) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci kmem_cache_free(fib6_node_kmem, fn); 1958c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_nodes--; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void node_free_rcu(struct rcu_head *head) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct fib6_node *fn = container_of(head, struct fib6_node, rcu); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci kmem_cache_free(fib6_node_kmem, fn); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void node_free(struct net *net, struct fib6_node *fn) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci call_rcu(&fn->rcu, node_free_rcu); 2088c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_nodes--; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void fib6_free_table(struct fib6_table *table) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci inetpeer_invalidate_tree(&table->tb6_peers); 2148c2ecf20Sopenharmony_ci kfree(table); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void fib6_link_table(struct net *net, struct fib6_table *tb) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci unsigned int h; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * Initialize table lock at a single place to give lockdep a key, 2238c2ecf20Sopenharmony_ci * tables aren't visible prior to being linked to the list. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci spin_lock_init(&tb->tb6_lock); 2268c2ecf20Sopenharmony_ci h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * No protection necessary, this is the only list mutatation 2308c2ecf20Sopenharmony_ci * operation, tables never disappear once they exist. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct fib6_table *fib6_alloc_table(struct net *net, u32 id) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct fib6_table *table; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci table = kzalloc(sizeof(*table), GFP_ATOMIC); 2428c2ecf20Sopenharmony_ci if (table) { 2438c2ecf20Sopenharmony_ci table->tb6_id = id; 2448c2ecf20Sopenharmony_ci rcu_assign_pointer(table->tb6_root.leaf, 2458c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry); 2468c2ecf20Sopenharmony_ci table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; 2478c2ecf20Sopenharmony_ci inet_peer_base_init(&table->tb6_peers); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return table; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct fib6_table *tb; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (id == 0) 2588c2ecf20Sopenharmony_ci id = RT6_TABLE_MAIN; 2598c2ecf20Sopenharmony_ci tb = fib6_get_table(net, id); 2608c2ecf20Sopenharmony_ci if (tb) 2618c2ecf20Sopenharmony_ci return tb; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci tb = fib6_alloc_table(net, id); 2648c2ecf20Sopenharmony_ci if (tb) 2658c2ecf20Sopenharmony_ci fib6_link_table(net, tb); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return tb; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_new_table); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct fib6_table *tb; 2748c2ecf20Sopenharmony_ci struct hlist_head *head; 2758c2ecf20Sopenharmony_ci unsigned int h; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (id == 0) 2788c2ecf20Sopenharmony_ci id = RT6_TABLE_MAIN; 2798c2ecf20Sopenharmony_ci h = id & (FIB6_TABLE_HASHSZ - 1); 2808c2ecf20Sopenharmony_ci rcu_read_lock(); 2818c2ecf20Sopenharmony_ci head = &net->ipv6.fib_table_hash[h]; 2828c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb6_hlist) { 2838c2ecf20Sopenharmony_ci if (tb->tb6_id == id) { 2848c2ecf20Sopenharmony_ci rcu_read_unlock(); 2858c2ecf20Sopenharmony_ci return tb; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci rcu_read_unlock(); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return NULL; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_get_table); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci fib6_link_table(net, net->ipv6.fib6_main_tbl); 2978c2ecf20Sopenharmony_ci fib6_link_table(net, net->ipv6.fib6_local_tbl); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci#else 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return fib6_get_table(net, id); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci return net->ipv6.fib6_main_tbl; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistruct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, 3128c2ecf20Sopenharmony_ci const struct sk_buff *skb, 3138c2ecf20Sopenharmony_ci int flags, pol_lookup_t lookup) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct rt6_info *rt; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rt = pol_lookup_func(lookup, 3188c2ecf20Sopenharmony_ci net, net->ipv6.fib6_main_tbl, fl6, skb, flags); 3198c2ecf20Sopenharmony_ci if (rt->dst.error == -EAGAIN) { 3208c2ecf20Sopenharmony_ci ip6_rt_put_flags(rt, flags); 3218c2ecf20Sopenharmony_ci rt = net->ipv6.ip6_null_entry; 3228c2ecf20Sopenharmony_ci if (!(flags & RT6_LOOKUP_F_DST_NOREF)) 3238c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return &rt->dst; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* called with rcu lock held; no reference taken on fib6_info */ 3308c2ecf20Sopenharmony_ciint fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, 3318c2ecf20Sopenharmony_ci struct fib6_result *res, int flags) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, 3348c2ecf20Sopenharmony_ci res, flags); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci fib6_link_table(net, net->ipv6.fib6_main_tbl); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci#endif 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciunsigned int fib6_tables_seq_read(struct net *net) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unsigned int h, fib_seq = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci rcu_read_lock(); 3498c2ecf20Sopenharmony_ci for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 3508c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv6.fib_table_hash[h]; 3518c2ecf20Sopenharmony_ci struct fib6_table *tb; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb6_hlist) 3548c2ecf20Sopenharmony_ci fib_seq += tb->fib_seq; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci rcu_read_unlock(); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return fib_seq; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int call_fib6_entry_notifier(struct notifier_block *nb, 3628c2ecf20Sopenharmony_ci enum fib_event_type event_type, 3638c2ecf20Sopenharmony_ci struct fib6_info *rt, 3648c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct fib6_entry_notifier_info info = { 3678c2ecf20Sopenharmony_ci .info.extack = extack, 3688c2ecf20Sopenharmony_ci .rt = rt, 3698c2ecf20Sopenharmony_ci }; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return call_fib6_notifier(nb, event_type, &info.info); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int call_fib6_multipath_entry_notifier(struct notifier_block *nb, 3758c2ecf20Sopenharmony_ci enum fib_event_type event_type, 3768c2ecf20Sopenharmony_ci struct fib6_info *rt, 3778c2ecf20Sopenharmony_ci unsigned int nsiblings, 3788c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct fib6_entry_notifier_info info = { 3818c2ecf20Sopenharmony_ci .info.extack = extack, 3828c2ecf20Sopenharmony_ci .rt = rt, 3838c2ecf20Sopenharmony_ci .nsiblings = nsiblings, 3848c2ecf20Sopenharmony_ci }; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return call_fib6_notifier(nb, event_type, &info.info); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciint call_fib6_entry_notifiers(struct net *net, 3908c2ecf20Sopenharmony_ci enum fib_event_type event_type, 3918c2ecf20Sopenharmony_ci struct fib6_info *rt, 3928c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct fib6_entry_notifier_info info = { 3958c2ecf20Sopenharmony_ci .info.extack = extack, 3968c2ecf20Sopenharmony_ci .rt = rt, 3978c2ecf20Sopenharmony_ci }; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci rt->fib6_table->fib_seq++; 4008c2ecf20Sopenharmony_ci return call_fib6_notifiers(net, event_type, &info.info); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciint call_fib6_multipath_entry_notifiers(struct net *net, 4048c2ecf20Sopenharmony_ci enum fib_event_type event_type, 4058c2ecf20Sopenharmony_ci struct fib6_info *rt, 4068c2ecf20Sopenharmony_ci unsigned int nsiblings, 4078c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct fib6_entry_notifier_info info = { 4108c2ecf20Sopenharmony_ci .info.extack = extack, 4118c2ecf20Sopenharmony_ci .rt = rt, 4128c2ecf20Sopenharmony_ci .nsiblings = nsiblings, 4138c2ecf20Sopenharmony_ci }; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci rt->fib6_table->fib_seq++; 4168c2ecf20Sopenharmony_ci return call_fib6_notifiers(net, event_type, &info.info); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ciint call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct fib6_entry_notifier_info info = { 4228c2ecf20Sopenharmony_ci .rt = rt, 4238c2ecf20Sopenharmony_ci .nsiblings = rt->fib6_nsiblings, 4248c2ecf20Sopenharmony_ci }; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci rt->fib6_table->fib_seq++; 4278c2ecf20Sopenharmony_ci return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistruct fib6_dump_arg { 4318c2ecf20Sopenharmony_ci struct net *net; 4328c2ecf20Sopenharmony_ci struct notifier_block *nb; 4338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack; 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE; 4398c2ecf20Sopenharmony_ci int err; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (!rt || rt == arg->net->ipv6.fib6_null_entry) 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings) 4458c2ecf20Sopenharmony_ci err = call_fib6_multipath_entry_notifier(arg->nb, fib_event, 4468c2ecf20Sopenharmony_ci rt, 4478c2ecf20Sopenharmony_ci rt->fib6_nsiblings, 4488c2ecf20Sopenharmony_ci arg->extack); 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci err = call_fib6_entry_notifier(arg->nb, fib_event, rt, 4518c2ecf20Sopenharmony_ci arg->extack); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return err; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int fib6_node_dump(struct fib6_walker *w) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci int err; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci err = fib6_rt_dump(w->leaf, w->args); 4618c2ecf20Sopenharmony_ci w->leaf = NULL; 4628c2ecf20Sopenharmony_ci return err; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int fib6_table_dump(struct net *net, struct fib6_table *tb, 4668c2ecf20Sopenharmony_ci struct fib6_walker *w) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci int err; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci w->root = &tb->tb6_root; 4718c2ecf20Sopenharmony_ci spin_lock_bh(&tb->tb6_lock); 4728c2ecf20Sopenharmony_ci err = fib6_walk(net, w); 4738c2ecf20Sopenharmony_ci spin_unlock_bh(&tb->tb6_lock); 4748c2ecf20Sopenharmony_ci return err; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* Called with rcu_read_lock() */ 4788c2ecf20Sopenharmony_ciint fib6_tables_dump(struct net *net, struct notifier_block *nb, 4798c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct fib6_dump_arg arg; 4828c2ecf20Sopenharmony_ci struct fib6_walker *w; 4838c2ecf20Sopenharmony_ci unsigned int h; 4848c2ecf20Sopenharmony_ci int err = 0; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci w = kzalloc(sizeof(*w), GFP_ATOMIC); 4878c2ecf20Sopenharmony_ci if (!w) 4888c2ecf20Sopenharmony_ci return -ENOMEM; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci w->func = fib6_node_dump; 4918c2ecf20Sopenharmony_ci arg.net = net; 4928c2ecf20Sopenharmony_ci arg.nb = nb; 4938c2ecf20Sopenharmony_ci arg.extack = extack; 4948c2ecf20Sopenharmony_ci w->args = &arg; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 4978c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv6.fib_table_hash[h]; 4988c2ecf20Sopenharmony_ci struct fib6_table *tb; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb6_hlist) { 5018c2ecf20Sopenharmony_ci err = fib6_table_dump(net, tb, w); 5028c2ecf20Sopenharmony_ci if (err) 5038c2ecf20Sopenharmony_ci goto out; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ciout: 5088c2ecf20Sopenharmony_ci kfree(w); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* The tree traversal function should never return a positive value. */ 5118c2ecf20Sopenharmony_ci return err > 0 ? -EINVAL : err; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic int fib6_dump_node(struct fib6_walker *w) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int res; 5178c2ecf20Sopenharmony_ci struct fib6_info *rt; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for_each_fib6_walker_rt(w) { 5208c2ecf20Sopenharmony_ci res = rt6_dump_route(rt, w->args, w->skip_in_node); 5218c2ecf20Sopenharmony_ci if (res >= 0) { 5228c2ecf20Sopenharmony_ci /* Frame is full, suspend walking */ 5238c2ecf20Sopenharmony_ci w->leaf = rt; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* We'll restart from this node, so if some routes were 5268c2ecf20Sopenharmony_ci * already dumped, skip them next time. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci w->skip_in_node += res; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 1; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci w->skip_in_node = 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Multipath routes are dumped in one route with the 5358c2ecf20Sopenharmony_ci * RTA_MULTIPATH attribute. Jump 'rt' to point to the 5368c2ecf20Sopenharmony_ci * last sibling of this route (no need to dump the 5378c2ecf20Sopenharmony_ci * sibling routes again) 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings) 5408c2ecf20Sopenharmony_ci rt = list_last_entry(&rt->fib6_siblings, 5418c2ecf20Sopenharmony_ci struct fib6_info, 5428c2ecf20Sopenharmony_ci fib6_siblings); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci w->leaf = NULL; 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic void fib6_dump_end(struct netlink_callback *cb) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 5518c2ecf20Sopenharmony_ci struct fib6_walker *w = (void *)cb->args[2]; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (w) { 5548c2ecf20Sopenharmony_ci if (cb->args[4]) { 5558c2ecf20Sopenharmony_ci cb->args[4] = 0; 5568c2ecf20Sopenharmony_ci fib6_walker_unlink(net, w); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci cb->args[2] = 0; 5598c2ecf20Sopenharmony_ci kfree(w); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci cb->done = (void *)cb->args[3]; 5628c2ecf20Sopenharmony_ci cb->args[1] = 3; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int fib6_dump_done(struct netlink_callback *cb) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci fib6_dump_end(cb); 5688c2ecf20Sopenharmony_ci return cb->done ? cb->done(cb) : 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, 5728c2ecf20Sopenharmony_ci struct netlink_callback *cb) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 5758c2ecf20Sopenharmony_ci struct fib6_walker *w; 5768c2ecf20Sopenharmony_ci int res; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci w = (void *)cb->args[2]; 5798c2ecf20Sopenharmony_ci w->root = &table->tb6_root; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (cb->args[4] == 0) { 5828c2ecf20Sopenharmony_ci w->count = 0; 5838c2ecf20Sopenharmony_ci w->skip = 0; 5848c2ecf20Sopenharmony_ci w->skip_in_node = 0; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 5878c2ecf20Sopenharmony_ci res = fib6_walk(net, w); 5888c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 5898c2ecf20Sopenharmony_ci if (res > 0) { 5908c2ecf20Sopenharmony_ci cb->args[4] = 1; 5918c2ecf20Sopenharmony_ci cb->args[5] = READ_ONCE(w->root->fn_sernum); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci } else { 5948c2ecf20Sopenharmony_ci int sernum = READ_ONCE(w->root->fn_sernum); 5958c2ecf20Sopenharmony_ci if (cb->args[5] != sernum) { 5968c2ecf20Sopenharmony_ci /* Begin at the root if the tree changed */ 5978c2ecf20Sopenharmony_ci cb->args[5] = sernum; 5988c2ecf20Sopenharmony_ci w->state = FWS_INIT; 5998c2ecf20Sopenharmony_ci w->node = w->root; 6008c2ecf20Sopenharmony_ci w->skip = w->count; 6018c2ecf20Sopenharmony_ci w->skip_in_node = 0; 6028c2ecf20Sopenharmony_ci } else 6038c2ecf20Sopenharmony_ci w->skip = 0; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 6068c2ecf20Sopenharmony_ci res = fib6_walk_continue(w); 6078c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 6088c2ecf20Sopenharmony_ci if (res <= 0) { 6098c2ecf20Sopenharmony_ci fib6_walker_unlink(net, w); 6108c2ecf20Sopenharmony_ci cb->args[4] = 0; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return res; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true, 6208c2ecf20Sopenharmony_ci .filter.dump_routes = true }; 6218c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 6228c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 6238c2ecf20Sopenharmony_ci unsigned int h, s_h; 6248c2ecf20Sopenharmony_ci unsigned int e = 0, s_e; 6258c2ecf20Sopenharmony_ci struct fib6_walker *w; 6268c2ecf20Sopenharmony_ci struct fib6_table *tb; 6278c2ecf20Sopenharmony_ci struct hlist_head *head; 6288c2ecf20Sopenharmony_ci int res = 0; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (cb->strict_check) { 6318c2ecf20Sopenharmony_ci int err; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb); 6348c2ecf20Sopenharmony_ci if (err < 0) 6358c2ecf20Sopenharmony_ci return err; 6368c2ecf20Sopenharmony_ci } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { 6378c2ecf20Sopenharmony_ci struct rtmsg *rtm = nlmsg_data(nlh); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (rtm->rtm_flags & RTM_F_PREFIX) 6408c2ecf20Sopenharmony_ci arg.filter.flags = RTM_F_PREFIX; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci w = (void *)cb->args[2]; 6448c2ecf20Sopenharmony_ci if (!w) { 6458c2ecf20Sopenharmony_ci /* New dump: 6468c2ecf20Sopenharmony_ci * 6478c2ecf20Sopenharmony_ci * 1. allocate and initialize walker. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci w = kzalloc(sizeof(*w), GFP_ATOMIC); 6508c2ecf20Sopenharmony_ci if (!w) 6518c2ecf20Sopenharmony_ci return -ENOMEM; 6528c2ecf20Sopenharmony_ci w->func = fib6_dump_node; 6538c2ecf20Sopenharmony_ci cb->args[2] = (long)w; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* 2. hook callback destructor. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci cb->args[3] = (long)cb->done; 6588c2ecf20Sopenharmony_ci cb->done = fib6_dump_done; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci arg.skb = skb; 6638c2ecf20Sopenharmony_ci arg.cb = cb; 6648c2ecf20Sopenharmony_ci arg.net = net; 6658c2ecf20Sopenharmony_ci w->args = &arg; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (arg.filter.table_id) { 6688c2ecf20Sopenharmony_ci tb = fib6_get_table(net, arg.filter.table_id); 6698c2ecf20Sopenharmony_ci if (!tb) { 6708c2ecf20Sopenharmony_ci if (rtnl_msg_family(cb->nlh) != PF_INET6) 6718c2ecf20Sopenharmony_ci goto out; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); 6748c2ecf20Sopenharmony_ci return -ENOENT; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (!cb->args[0]) { 6788c2ecf20Sopenharmony_ci res = fib6_dump_table(tb, skb, cb); 6798c2ecf20Sopenharmony_ci if (!res) 6808c2ecf20Sopenharmony_ci cb->args[0] = 1; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci goto out; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci s_h = cb->args[0]; 6868c2ecf20Sopenharmony_ci s_e = cb->args[1]; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci rcu_read_lock(); 6898c2ecf20Sopenharmony_ci for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { 6908c2ecf20Sopenharmony_ci e = 0; 6918c2ecf20Sopenharmony_ci head = &net->ipv6.fib_table_hash[h]; 6928c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb6_hlist) { 6938c2ecf20Sopenharmony_ci if (e < s_e) 6948c2ecf20Sopenharmony_ci goto next; 6958c2ecf20Sopenharmony_ci res = fib6_dump_table(tb, skb, cb); 6968c2ecf20Sopenharmony_ci if (res != 0) 6978c2ecf20Sopenharmony_ci goto out_unlock; 6988c2ecf20Sopenharmony_cinext: 6998c2ecf20Sopenharmony_ci e++; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ciout_unlock: 7038c2ecf20Sopenharmony_ci rcu_read_unlock(); 7048c2ecf20Sopenharmony_ci cb->args[1] = e; 7058c2ecf20Sopenharmony_ci cb->args[0] = h; 7068c2ecf20Sopenharmony_ciout: 7078c2ecf20Sopenharmony_ci res = res < 0 ? res : skb->len; 7088c2ecf20Sopenharmony_ci if (res <= 0) 7098c2ecf20Sopenharmony_ci fib6_dump_end(cb); 7108c2ecf20Sopenharmony_ci return res; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_civoid fib6_metric_set(struct fib6_info *f6i, int metric, u32 val) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci if (!f6i) 7168c2ecf20Sopenharmony_ci return; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (f6i->fib6_metrics == &dst_default_metrics) { 7198c2ecf20Sopenharmony_ci struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!p) 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci refcount_set(&p->refcnt, 1); 7258c2ecf20Sopenharmony_ci f6i->fib6_metrics = p; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci f6i->fib6_metrics->metrics[metric - 1] = val; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci/* 7328c2ecf20Sopenharmony_ci * Routing Table 7338c2ecf20Sopenharmony_ci * 7348c2ecf20Sopenharmony_ci * return the appropriate node for a routing tree "add" operation 7358c2ecf20Sopenharmony_ci * by either creating and inserting or by returning an existing 7368c2ecf20Sopenharmony_ci * node. 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_add_1(struct net *net, 7408c2ecf20Sopenharmony_ci struct fib6_table *table, 7418c2ecf20Sopenharmony_ci struct fib6_node *root, 7428c2ecf20Sopenharmony_ci struct in6_addr *addr, int plen, 7438c2ecf20Sopenharmony_ci int offset, int allow_create, 7448c2ecf20Sopenharmony_ci int replace_required, 7458c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct fib6_node *fn, *in, *ln; 7488c2ecf20Sopenharmony_ci struct fib6_node *pn = NULL; 7498c2ecf20Sopenharmony_ci struct rt6key *key; 7508c2ecf20Sopenharmony_ci int bit; 7518c2ecf20Sopenharmony_ci __be32 dir = 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci RT6_TRACE("fib6_add_1\n"); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* insert node in tree */ 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci fn = root; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci do { 7608c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference_protected(fn->leaf, 7618c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 7628c2ecf20Sopenharmony_ci key = (struct rt6key *)((u8 *)leaf + offset); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* 7658c2ecf20Sopenharmony_ci * Prefix match 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_ci if (plen < fn->fn_bit || 7688c2ecf20Sopenharmony_ci !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) { 7698c2ecf20Sopenharmony_ci if (!allow_create) { 7708c2ecf20Sopenharmony_ci if (replace_required) { 7718c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 7728c2ecf20Sopenharmony_ci "Can not replace route - no match found"); 7738c2ecf20Sopenharmony_ci pr_warn("Can't replace route, no match found\n"); 7748c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci pr_warn("NLM_F_CREATE should be set when creating new route\n"); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci goto insert_above; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* 7828c2ecf20Sopenharmony_ci * Exact match ? 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (plen == fn->fn_bit) { 7868c2ecf20Sopenharmony_ci /* clean up an intermediate node */ 7878c2ecf20Sopenharmony_ci if (!(fn->fn_flags & RTN_RTINFO)) { 7888c2ecf20Sopenharmony_ci RCU_INIT_POINTER(fn->leaf, NULL); 7898c2ecf20Sopenharmony_ci fib6_info_release(leaf); 7908c2ecf20Sopenharmony_ci /* remove null_entry in the root node */ 7918c2ecf20Sopenharmony_ci } else if (fn->fn_flags & RTN_TL_ROOT && 7928c2ecf20Sopenharmony_ci rcu_access_pointer(fn->leaf) == 7938c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry) { 7948c2ecf20Sopenharmony_ci RCU_INIT_POINTER(fn->leaf, NULL); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return fn; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* 8018c2ecf20Sopenharmony_ci * We have more bits to go 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Try to walk down on tree. */ 8058c2ecf20Sopenharmony_ci dir = addr_bit_set(addr, fn->fn_bit); 8068c2ecf20Sopenharmony_ci pn = fn; 8078c2ecf20Sopenharmony_ci fn = dir ? 8088c2ecf20Sopenharmony_ci rcu_dereference_protected(fn->right, 8098c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)) : 8108c2ecf20Sopenharmony_ci rcu_dereference_protected(fn->left, 8118c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 8128c2ecf20Sopenharmony_ci } while (fn); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (!allow_create) { 8158c2ecf20Sopenharmony_ci /* We should not create new node because 8168c2ecf20Sopenharmony_ci * NLM_F_REPLACE was specified without NLM_F_CREATE 8178c2ecf20Sopenharmony_ci * I assume it is safe to require NLM_F_CREATE when 8188c2ecf20Sopenharmony_ci * REPLACE flag is used! Later we may want to remove the 8198c2ecf20Sopenharmony_ci * check for replace_required, because according 8208c2ecf20Sopenharmony_ci * to netlink specification, NLM_F_CREATE 8218c2ecf20Sopenharmony_ci * MUST be specified if new route is created. 8228c2ecf20Sopenharmony_ci * That would keep IPv6 consistent with IPv4 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci if (replace_required) { 8258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 8268c2ecf20Sopenharmony_ci "Can not replace route - no match found"); 8278c2ecf20Sopenharmony_ci pr_warn("Can't replace route, no match found\n"); 8288c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci pr_warn("NLM_F_CREATE should be set when creating new route\n"); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * We walked to the bottom of tree. 8348c2ecf20Sopenharmony_ci * Create new leaf node without children. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ln = node_alloc(net); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (!ln) 8408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8418c2ecf20Sopenharmony_ci ln->fn_bit = plen; 8428c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ln->parent, pn); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (dir) 8458c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->right, ln); 8468c2ecf20Sopenharmony_ci else 8478c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->left, ln); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return ln; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ciinsert_above: 8538c2ecf20Sopenharmony_ci /* 8548c2ecf20Sopenharmony_ci * split since we don't have a common prefix anymore or 8558c2ecf20Sopenharmony_ci * we have a less significant route. 8568c2ecf20Sopenharmony_ci * we've to insert an intermediate node on the list 8578c2ecf20Sopenharmony_ci * this new node will point to the one we need to create 8588c2ecf20Sopenharmony_ci * and the current 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci pn = rcu_dereference_protected(fn->parent, 8628c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* find 1st bit in difference between the 2 addrs. 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci See comment in __ipv6_addr_diff: bit may be an invalid value, 8678c2ecf20Sopenharmony_ci but if it is >= plen, the value is ignored in any case. 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci bit = __ipv6_addr_diff(addr, &key->addr, sizeof(*addr)); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* 8738c2ecf20Sopenharmony_ci * (intermediate)[in] 8748c2ecf20Sopenharmony_ci * / \ 8758c2ecf20Sopenharmony_ci * (new leaf node)[ln] (old node)[fn] 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci if (plen > bit) { 8788c2ecf20Sopenharmony_ci in = node_alloc(net); 8798c2ecf20Sopenharmony_ci ln = node_alloc(net); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (!in || !ln) { 8828c2ecf20Sopenharmony_ci if (in) 8838c2ecf20Sopenharmony_ci node_free_immediate(net, in); 8848c2ecf20Sopenharmony_ci if (ln) 8858c2ecf20Sopenharmony_ci node_free_immediate(net, ln); 8868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* 8908c2ecf20Sopenharmony_ci * new intermediate node. 8918c2ecf20Sopenharmony_ci * RTN_RTINFO will 8928c2ecf20Sopenharmony_ci * be off since that an address that chooses one of 8938c2ecf20Sopenharmony_ci * the branches would not match less specific routes 8948c2ecf20Sopenharmony_ci * in the other branch 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci in->fn_bit = bit; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci RCU_INIT_POINTER(in->parent, pn); 9008c2ecf20Sopenharmony_ci in->leaf = fn->leaf; 9018c2ecf20Sopenharmony_ci fib6_info_hold(rcu_dereference_protected(in->leaf, 9028c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock))); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* update parent pointer */ 9058c2ecf20Sopenharmony_ci if (dir) 9068c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->right, in); 9078c2ecf20Sopenharmony_ci else 9088c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->left, in); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ln->fn_bit = plen; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ln->parent, in); 9138c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->parent, in); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (addr_bit_set(addr, bit)) { 9168c2ecf20Sopenharmony_ci rcu_assign_pointer(in->right, ln); 9178c2ecf20Sopenharmony_ci rcu_assign_pointer(in->left, fn); 9188c2ecf20Sopenharmony_ci } else { 9198c2ecf20Sopenharmony_ci rcu_assign_pointer(in->left, ln); 9208c2ecf20Sopenharmony_ci rcu_assign_pointer(in->right, fn); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci } else { /* plen <= bit */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* 9258c2ecf20Sopenharmony_ci * (new leaf node)[ln] 9268c2ecf20Sopenharmony_ci * / \ 9278c2ecf20Sopenharmony_ci * (old node)[fn] NULL 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci ln = node_alloc(net); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (!ln) 9338c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci ln->fn_bit = plen; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ln->parent, pn); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (addr_bit_set(&key->addr, plen)) 9408c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ln->right, fn); 9418c2ecf20Sopenharmony_ci else 9428c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ln->left, fn); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->parent, ln); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (dir) 9478c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->right, ln); 9488c2ecf20Sopenharmony_ci else 9498c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->left, ln); 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci return ln; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, 9558c2ecf20Sopenharmony_ci const struct fib6_info *match, 9568c2ecf20Sopenharmony_ci const struct fib6_table *table) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci int cpu; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (!fib6_nh->rt6i_pcpu) 9618c2ecf20Sopenharmony_ci return; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci rcu_read_lock(); 9648c2ecf20Sopenharmony_ci /* release the reference to this fib entry from 9658c2ecf20Sopenharmony_ci * all of its cached pcpu routes 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 9688c2ecf20Sopenharmony_ci struct rt6_info **ppcpu_rt; 9698c2ecf20Sopenharmony_ci struct rt6_info *pcpu_rt; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Paired with xchg() in rt6_get_pcpu_route() */ 9748c2ecf20Sopenharmony_ci pcpu_rt = READ_ONCE(*ppcpu_rt); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* only dropping the 'from' reference if the cached route 9778c2ecf20Sopenharmony_ci * is using 'match'. The cached pcpu_rt->from only changes 9788c2ecf20Sopenharmony_ci * from a fib6_info to NULL (ip6_dst_destroy); it can never 9798c2ecf20Sopenharmony_ci * change from one fib6_info reference to another 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ci if (pcpu_rt && rcu_access_pointer(pcpu_rt->from) == match) { 9828c2ecf20Sopenharmony_ci struct fib6_info *from; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL); 9858c2ecf20Sopenharmony_ci fib6_info_release(from); 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci rcu_read_unlock(); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistruct fib6_nh_pcpu_arg { 9928c2ecf20Sopenharmony_ci struct fib6_info *from; 9938c2ecf20Sopenharmony_ci const struct fib6_table *table; 9948c2ecf20Sopenharmony_ci}; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct fib6_nh_pcpu_arg *arg = _arg; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci __fib6_drop_pcpu_from(nh, arg->from, arg->table); 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void fib6_drop_pcpu_from(struct fib6_info *f6i, 10058c2ecf20Sopenharmony_ci const struct fib6_table *table) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci /* Make sure rt6_make_pcpu_route() wont add other percpu routes 10088c2ecf20Sopenharmony_ci * while we are cleaning them here. 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_ci f6i->fib6_destroying = 1; 10118c2ecf20Sopenharmony_ci mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */ 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (f6i->nh) { 10148c2ecf20Sopenharmony_ci struct fib6_nh_pcpu_arg arg = { 10158c2ecf20Sopenharmony_ci .from = f6i, 10168c2ecf20Sopenharmony_ci .table = table 10178c2ecf20Sopenharmony_ci }; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_drop_pcpu_from, 10208c2ecf20Sopenharmony_ci &arg); 10218c2ecf20Sopenharmony_ci } else { 10228c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci fib6_nh = f6i->fib6_nh; 10258c2ecf20Sopenharmony_ci __fib6_drop_pcpu_from(fib6_nh, f6i, table); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn, 10308c2ecf20Sopenharmony_ci struct net *net) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct fib6_table *table = rt->fib6_table; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* Flush all cached dst in exception table */ 10358c2ecf20Sopenharmony_ci rt6_flush_exceptions(rt); 10368c2ecf20Sopenharmony_ci fib6_drop_pcpu_from(rt, table); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (rt->nh && !list_empty(&rt->nh_list)) 10398c2ecf20Sopenharmony_ci list_del_init(&rt->nh_list); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (refcount_read(&rt->fib6_ref) != 1) { 10428c2ecf20Sopenharmony_ci /* This route is used as dummy address holder in some split 10438c2ecf20Sopenharmony_ci * nodes. It is not leaked, but it still holds other resources, 10448c2ecf20Sopenharmony_ci * which must be released in time. So, scan ascendant nodes 10458c2ecf20Sopenharmony_ci * and replace dummy references to this route with references 10468c2ecf20Sopenharmony_ci * to still alive ones. 10478c2ecf20Sopenharmony_ci */ 10488c2ecf20Sopenharmony_ci while (fn) { 10498c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference_protected(fn->leaf, 10508c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 10518c2ecf20Sopenharmony_ci struct fib6_info *new_leaf; 10528c2ecf20Sopenharmony_ci if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) { 10538c2ecf20Sopenharmony_ci new_leaf = fib6_find_prefix(net, table, fn); 10548c2ecf20Sopenharmony_ci fib6_info_hold(new_leaf); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->leaf, new_leaf); 10578c2ecf20Sopenharmony_ci fib6_info_release(rt); 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(fn->parent, 10608c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * Insert routing information in a node. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt, 10708c2ecf20Sopenharmony_ci struct nl_info *info, 10718c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference_protected(fn->leaf, 10748c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 10758c2ecf20Sopenharmony_ci struct fib6_info *iter = NULL; 10768c2ecf20Sopenharmony_ci struct fib6_info __rcu **ins; 10778c2ecf20Sopenharmony_ci struct fib6_info __rcu **fallback_ins = NULL; 10788c2ecf20Sopenharmony_ci int replace = (info->nlh && 10798c2ecf20Sopenharmony_ci (info->nlh->nlmsg_flags & NLM_F_REPLACE)); 10808c2ecf20Sopenharmony_ci int add = (!info->nlh || 10818c2ecf20Sopenharmony_ci (info->nlh->nlmsg_flags & NLM_F_CREATE)); 10828c2ecf20Sopenharmony_ci int found = 0; 10838c2ecf20Sopenharmony_ci bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); 10848c2ecf20Sopenharmony_ci bool notify_sibling_rt = false; 10858c2ecf20Sopenharmony_ci u16 nlflags = NLM_F_EXCL; 10868c2ecf20Sopenharmony_ci int err; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (info->nlh && (info->nlh->nlmsg_flags & NLM_F_APPEND)) 10898c2ecf20Sopenharmony_ci nlflags |= NLM_F_APPEND; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci ins = &fn->leaf; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci for (iter = leaf; iter; 10948c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(iter->fib6_next, 10958c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock))) { 10968c2ecf20Sopenharmony_ci /* 10978c2ecf20Sopenharmony_ci * Search for duplicates 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (iter->fib6_metric == rt->fib6_metric) { 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * Same priority level 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci if (info->nlh && 11058c2ecf20Sopenharmony_ci (info->nlh->nlmsg_flags & NLM_F_EXCL)) 11068c2ecf20Sopenharmony_ci return -EEXIST; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci nlflags &= ~NLM_F_EXCL; 11098c2ecf20Sopenharmony_ci if (replace) { 11108c2ecf20Sopenharmony_ci if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) { 11118c2ecf20Sopenharmony_ci found++; 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci fallback_ins = fallback_ins ?: ins; 11158c2ecf20Sopenharmony_ci goto next_iter; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (rt6_duplicate_nexthop(iter, rt)) { 11198c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings) 11208c2ecf20Sopenharmony_ci rt->fib6_nsiblings = 0; 11218c2ecf20Sopenharmony_ci if (!(iter->fib6_flags & RTF_EXPIRES)) 11228c2ecf20Sopenharmony_ci return -EEXIST; 11238c2ecf20Sopenharmony_ci if (!(rt->fib6_flags & RTF_EXPIRES)) 11248c2ecf20Sopenharmony_ci fib6_clean_expires(iter); 11258c2ecf20Sopenharmony_ci else 11268c2ecf20Sopenharmony_ci fib6_set_expires(iter, rt->expires); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (rt->fib6_pmtu) 11298c2ecf20Sopenharmony_ci fib6_metric_set(iter, RTAX_MTU, 11308c2ecf20Sopenharmony_ci rt->fib6_pmtu); 11318c2ecf20Sopenharmony_ci return -EEXIST; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci /* If we have the same destination and the same metric, 11348c2ecf20Sopenharmony_ci * but not the same gateway, then the route we try to 11358c2ecf20Sopenharmony_ci * add is sibling to this route, increment our counter 11368c2ecf20Sopenharmony_ci * of siblings, and later we will add our route to the 11378c2ecf20Sopenharmony_ci * list. 11388c2ecf20Sopenharmony_ci * Only static routes (which don't have flag 11398c2ecf20Sopenharmony_ci * RTF_EXPIRES) are used for ECMPv6. 11408c2ecf20Sopenharmony_ci * 11418c2ecf20Sopenharmony_ci * To avoid long list, we only had siblings if the 11428c2ecf20Sopenharmony_ci * route have a gateway. 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci if (rt_can_ecmp && 11458c2ecf20Sopenharmony_ci rt6_qualify_for_ecmp(iter)) 11468c2ecf20Sopenharmony_ci rt->fib6_nsiblings++; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (iter->fib6_metric > rt->fib6_metric) 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cinext_iter: 11538c2ecf20Sopenharmony_ci ins = &iter->fib6_next; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (fallback_ins && !found) { 11578c2ecf20Sopenharmony_ci /* No matching route with same ecmp-able-ness found, replace 11588c2ecf20Sopenharmony_ci * first matching route 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_ci ins = fallback_ins; 11618c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(*ins, 11628c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 11638c2ecf20Sopenharmony_ci found++; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Reset round-robin state, if necessary */ 11678c2ecf20Sopenharmony_ci if (ins == &fn->leaf) 11688c2ecf20Sopenharmony_ci fn->rr_ptr = NULL; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* Link this route to others same route. */ 11718c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings) { 11728c2ecf20Sopenharmony_ci unsigned int fib6_nsiblings; 11738c2ecf20Sopenharmony_ci struct fib6_info *sibling, *temp_sibling; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* Find the first route that have the same metric */ 11768c2ecf20Sopenharmony_ci sibling = leaf; 11778c2ecf20Sopenharmony_ci notify_sibling_rt = true; 11788c2ecf20Sopenharmony_ci while (sibling) { 11798c2ecf20Sopenharmony_ci if (sibling->fib6_metric == rt->fib6_metric && 11808c2ecf20Sopenharmony_ci rt6_qualify_for_ecmp(sibling)) { 11818c2ecf20Sopenharmony_ci list_add_tail(&rt->fib6_siblings, 11828c2ecf20Sopenharmony_ci &sibling->fib6_siblings); 11838c2ecf20Sopenharmony_ci break; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci sibling = rcu_dereference_protected(sibling->fib6_next, 11868c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 11878c2ecf20Sopenharmony_ci notify_sibling_rt = false; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci /* For each sibling in the list, increment the counter of 11908c2ecf20Sopenharmony_ci * siblings. BUG() if counters does not match, list of siblings 11918c2ecf20Sopenharmony_ci * is broken! 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_ci fib6_nsiblings = 0; 11948c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, temp_sibling, 11958c2ecf20Sopenharmony_ci &rt->fib6_siblings, fib6_siblings) { 11968c2ecf20Sopenharmony_ci sibling->fib6_nsiblings++; 11978c2ecf20Sopenharmony_ci BUG_ON(sibling->fib6_nsiblings != rt->fib6_nsiblings); 11988c2ecf20Sopenharmony_ci fib6_nsiblings++; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci BUG_ON(fib6_nsiblings != rt->fib6_nsiblings); 12018c2ecf20Sopenharmony_ci rt6_multipath_rebalance(temp_sibling); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* 12058c2ecf20Sopenharmony_ci * insert node 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_ci if (!replace) { 12088c2ecf20Sopenharmony_ci if (!add) 12098c2ecf20Sopenharmony_ci pr_warn("NLM_F_CREATE should be set when creating new route\n"); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ciadd: 12128c2ecf20Sopenharmony_ci nlflags |= NLM_F_CREATE; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* The route should only be notified if it is the first 12158c2ecf20Sopenharmony_ci * route in the node or if it is added as a sibling 12168c2ecf20Sopenharmony_ci * route to the first route in the node. 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci if (!info->skip_notify_kernel && 12198c2ecf20Sopenharmony_ci (notify_sibling_rt || ins == &fn->leaf)) { 12208c2ecf20Sopenharmony_ci enum fib_event_type fib_event; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (notify_sibling_rt) 12238c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_APPEND; 12248c2ecf20Sopenharmony_ci else 12258c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 12268c2ecf20Sopenharmony_ci err = call_fib6_entry_notifiers(info->nl_net, 12278c2ecf20Sopenharmony_ci fib_event, rt, 12288c2ecf20Sopenharmony_ci extack); 12298c2ecf20Sopenharmony_ci if (err) { 12308c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* If the route has siblings, then it first 12338c2ecf20Sopenharmony_ci * needs to be unlinked from them. 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_ci if (!rt->fib6_nsiblings) 12368c2ecf20Sopenharmony_ci return err; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 12398c2ecf20Sopenharmony_ci &rt->fib6_siblings, 12408c2ecf20Sopenharmony_ci fib6_siblings) 12418c2ecf20Sopenharmony_ci sibling->fib6_nsiblings--; 12428c2ecf20Sopenharmony_ci rt->fib6_nsiblings = 0; 12438c2ecf20Sopenharmony_ci list_del_init(&rt->fib6_siblings); 12448c2ecf20Sopenharmony_ci rt6_multipath_rebalance(next_sibling); 12458c2ecf20Sopenharmony_ci return err; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci rcu_assign_pointer(rt->fib6_next, iter); 12508c2ecf20Sopenharmony_ci fib6_info_hold(rt); 12518c2ecf20Sopenharmony_ci rcu_assign_pointer(rt->fib6_node, fn); 12528c2ecf20Sopenharmony_ci rcu_assign_pointer(*ins, rt); 12538c2ecf20Sopenharmony_ci if (!info->skip_notify) 12548c2ecf20Sopenharmony_ci inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 12558c2ecf20Sopenharmony_ci info->nl_net->ipv6.rt6_stats->fib_rt_entries++; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci if (!(fn->fn_flags & RTN_RTINFO)) { 12588c2ecf20Sopenharmony_ci info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 12598c2ecf20Sopenharmony_ci fn->fn_flags |= RTN_RTINFO; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci } else { 12638c2ecf20Sopenharmony_ci int nsiblings; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (!found) { 12668c2ecf20Sopenharmony_ci if (add) 12678c2ecf20Sopenharmony_ci goto add; 12688c2ecf20Sopenharmony_ci pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); 12698c2ecf20Sopenharmony_ci return -ENOENT; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (!info->skip_notify_kernel && ins == &fn->leaf) { 12738c2ecf20Sopenharmony_ci err = call_fib6_entry_notifiers(info->nl_net, 12748c2ecf20Sopenharmony_ci FIB_EVENT_ENTRY_REPLACE, 12758c2ecf20Sopenharmony_ci rt, extack); 12768c2ecf20Sopenharmony_ci if (err) 12778c2ecf20Sopenharmony_ci return err; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci fib6_info_hold(rt); 12818c2ecf20Sopenharmony_ci rcu_assign_pointer(rt->fib6_node, fn); 12828c2ecf20Sopenharmony_ci rt->fib6_next = iter->fib6_next; 12838c2ecf20Sopenharmony_ci rcu_assign_pointer(*ins, rt); 12848c2ecf20Sopenharmony_ci if (!info->skip_notify) 12858c2ecf20Sopenharmony_ci inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE); 12868c2ecf20Sopenharmony_ci if (!(fn->fn_flags & RTN_RTINFO)) { 12878c2ecf20Sopenharmony_ci info->nl_net->ipv6.rt6_stats->fib_route_nodes++; 12888c2ecf20Sopenharmony_ci fn->fn_flags |= RTN_RTINFO; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci nsiblings = iter->fib6_nsiblings; 12918c2ecf20Sopenharmony_ci iter->fib6_node = NULL; 12928c2ecf20Sopenharmony_ci fib6_purge_rt(iter, fn, info->nl_net); 12938c2ecf20Sopenharmony_ci if (rcu_access_pointer(fn->rr_ptr) == iter) 12948c2ecf20Sopenharmony_ci fn->rr_ptr = NULL; 12958c2ecf20Sopenharmony_ci fib6_info_release(iter); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (nsiblings) { 12988c2ecf20Sopenharmony_ci /* Replacing an ECMP route, remove all siblings */ 12998c2ecf20Sopenharmony_ci ins = &rt->fib6_next; 13008c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(*ins, 13018c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 13028c2ecf20Sopenharmony_ci while (iter) { 13038c2ecf20Sopenharmony_ci if (iter->fib6_metric > rt->fib6_metric) 13048c2ecf20Sopenharmony_ci break; 13058c2ecf20Sopenharmony_ci if (rt6_qualify_for_ecmp(iter)) { 13068c2ecf20Sopenharmony_ci *ins = iter->fib6_next; 13078c2ecf20Sopenharmony_ci iter->fib6_node = NULL; 13088c2ecf20Sopenharmony_ci fib6_purge_rt(iter, fn, info->nl_net); 13098c2ecf20Sopenharmony_ci if (rcu_access_pointer(fn->rr_ptr) == iter) 13108c2ecf20Sopenharmony_ci fn->rr_ptr = NULL; 13118c2ecf20Sopenharmony_ci fib6_info_release(iter); 13128c2ecf20Sopenharmony_ci nsiblings--; 13138c2ecf20Sopenharmony_ci info->nl_net->ipv6.rt6_stats->fib_rt_entries--; 13148c2ecf20Sopenharmony_ci } else { 13158c2ecf20Sopenharmony_ci ins = &iter->fib6_next; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci iter = rcu_dereference_protected(*ins, 13188c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci WARN_ON(nsiblings != 0); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci return 0; 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic void fib6_start_gc(struct net *net, struct fib6_info *rt) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci if (!timer_pending(&net->ipv6.ip6_fib_timer) && 13308c2ecf20Sopenharmony_ci (rt->fib6_flags & RTF_EXPIRES)) 13318c2ecf20Sopenharmony_ci mod_timer(&net->ipv6.ip6_fib_timer, 13328c2ecf20Sopenharmony_ci jiffies + net->ipv6.sysctl.ip6_rt_gc_interval); 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_civoid fib6_force_start_gc(struct net *net) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci if (!timer_pending(&net->ipv6.ip6_fib_timer)) 13388c2ecf20Sopenharmony_ci mod_timer(&net->ipv6.ip6_fib_timer, 13398c2ecf20Sopenharmony_ci jiffies + net->ipv6.sysctl.ip6_rt_gc_interval); 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic void __fib6_update_sernum_upto_root(struct fib6_info *rt, 13438c2ecf20Sopenharmony_ci int sernum) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci struct fib6_node *fn = rcu_dereference_protected(rt->fib6_node, 13468c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* paired with smp_rmb() in rt6_get_cookie_safe() */ 13498c2ecf20Sopenharmony_ci smp_wmb(); 13508c2ecf20Sopenharmony_ci while (fn) { 13518c2ecf20Sopenharmony_ci WRITE_ONCE(fn->fn_sernum, sernum); 13528c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(fn->parent, 13538c2ecf20Sopenharmony_ci lockdep_is_held(&rt->fib6_table->tb6_lock)); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_civoid fib6_update_sernum_upto_root(struct net *net, struct fib6_info *rt) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci __fib6_update_sernum_upto_root(rt, fib6_new_sernum(net)); 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci/* allow ipv4 to update sernum via ipv6_stub */ 13638c2ecf20Sopenharmony_civoid fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci spin_lock_bh(&f6i->fib6_table->tb6_lock); 13668c2ecf20Sopenharmony_ci fib6_update_sernum_upto_root(net, f6i); 13678c2ecf20Sopenharmony_ci spin_unlock_bh(&f6i->fib6_table->tb6_lock); 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci/* 13718c2ecf20Sopenharmony_ci * Add routing information to the routing tree. 13728c2ecf20Sopenharmony_ci * <destination addr>/<source addr> 13738c2ecf20Sopenharmony_ci * with source addr info in sub-trees 13748c2ecf20Sopenharmony_ci * Need to own table->tb6_lock 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ciint fib6_add(struct fib6_node *root, struct fib6_info *rt, 13788c2ecf20Sopenharmony_ci struct nl_info *info, struct netlink_ext_ack *extack) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct fib6_table *table = rt->fib6_table; 13818c2ecf20Sopenharmony_ci struct fib6_node *fn, *pn = NULL; 13828c2ecf20Sopenharmony_ci int err = -ENOMEM; 13838c2ecf20Sopenharmony_ci int allow_create = 1; 13848c2ecf20Sopenharmony_ci int replace_required = 0; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (info->nlh) { 13878c2ecf20Sopenharmony_ci if (!(info->nlh->nlmsg_flags & NLM_F_CREATE)) 13888c2ecf20Sopenharmony_ci allow_create = 0; 13898c2ecf20Sopenharmony_ci if (info->nlh->nlmsg_flags & NLM_F_REPLACE) 13908c2ecf20Sopenharmony_ci replace_required = 1; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci if (!allow_create && !replace_required) 13938c2ecf20Sopenharmony_ci pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci fn = fib6_add_1(info->nl_net, table, root, 13968c2ecf20Sopenharmony_ci &rt->fib6_dst.addr, rt->fib6_dst.plen, 13978c2ecf20Sopenharmony_ci offsetof(struct fib6_info, fib6_dst), allow_create, 13988c2ecf20Sopenharmony_ci replace_required, extack); 13998c2ecf20Sopenharmony_ci if (IS_ERR(fn)) { 14008c2ecf20Sopenharmony_ci err = PTR_ERR(fn); 14018c2ecf20Sopenharmony_ci fn = NULL; 14028c2ecf20Sopenharmony_ci goto out; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci pn = fn; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 14088c2ecf20Sopenharmony_ci if (rt->fib6_src.plen) { 14098c2ecf20Sopenharmony_ci struct fib6_node *sn; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (!rcu_access_pointer(fn->subtree)) { 14128c2ecf20Sopenharmony_ci struct fib6_node *sfn; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* 14158c2ecf20Sopenharmony_ci * Create subtree. 14168c2ecf20Sopenharmony_ci * 14178c2ecf20Sopenharmony_ci * fn[main tree] 14188c2ecf20Sopenharmony_ci * | 14198c2ecf20Sopenharmony_ci * sfn[subtree root] 14208c2ecf20Sopenharmony_ci * \ 14218c2ecf20Sopenharmony_ci * sn[new leaf node] 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci /* Create subtree root node */ 14258c2ecf20Sopenharmony_ci sfn = node_alloc(info->nl_net); 14268c2ecf20Sopenharmony_ci if (!sfn) 14278c2ecf20Sopenharmony_ci goto failure; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci fib6_info_hold(info->nl_net->ipv6.fib6_null_entry); 14308c2ecf20Sopenharmony_ci rcu_assign_pointer(sfn->leaf, 14318c2ecf20Sopenharmony_ci info->nl_net->ipv6.fib6_null_entry); 14328c2ecf20Sopenharmony_ci sfn->fn_flags = RTN_ROOT; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* Now add the first leaf node to new subtree */ 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci sn = fib6_add_1(info->nl_net, table, sfn, 14378c2ecf20Sopenharmony_ci &rt->fib6_src.addr, rt->fib6_src.plen, 14388c2ecf20Sopenharmony_ci offsetof(struct fib6_info, fib6_src), 14398c2ecf20Sopenharmony_ci allow_create, replace_required, extack); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (IS_ERR(sn)) { 14428c2ecf20Sopenharmony_ci /* If it is failed, discard just allocated 14438c2ecf20Sopenharmony_ci root, and then (in failure) stale node 14448c2ecf20Sopenharmony_ci in main tree. 14458c2ecf20Sopenharmony_ci */ 14468c2ecf20Sopenharmony_ci node_free_immediate(info->nl_net, sfn); 14478c2ecf20Sopenharmony_ci err = PTR_ERR(sn); 14488c2ecf20Sopenharmony_ci goto failure; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* Now link new subtree to main tree */ 14528c2ecf20Sopenharmony_ci rcu_assign_pointer(sfn->parent, fn); 14538c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->subtree, sfn); 14548c2ecf20Sopenharmony_ci } else { 14558c2ecf20Sopenharmony_ci sn = fib6_add_1(info->nl_net, table, FIB6_SUBTREE(fn), 14568c2ecf20Sopenharmony_ci &rt->fib6_src.addr, rt->fib6_src.plen, 14578c2ecf20Sopenharmony_ci offsetof(struct fib6_info, fib6_src), 14588c2ecf20Sopenharmony_ci allow_create, replace_required, extack); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (IS_ERR(sn)) { 14618c2ecf20Sopenharmony_ci err = PTR_ERR(sn); 14628c2ecf20Sopenharmony_ci goto failure; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (!rcu_access_pointer(fn->leaf)) { 14678c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_TL_ROOT) { 14688c2ecf20Sopenharmony_ci /* put back null_entry for root node */ 14698c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->leaf, 14708c2ecf20Sopenharmony_ci info->nl_net->ipv6.fib6_null_entry); 14718c2ecf20Sopenharmony_ci } else { 14728c2ecf20Sopenharmony_ci fib6_info_hold(rt); 14738c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->leaf, rt); 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci fn = sn; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci#endif 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci err = fib6_add_rt2node(fn, rt, info, extack); 14818c2ecf20Sopenharmony_ci if (!err) { 14828c2ecf20Sopenharmony_ci if (rt->nh) 14838c2ecf20Sopenharmony_ci list_add(&rt->nh_list, &rt->nh->f6i_list); 14848c2ecf20Sopenharmony_ci __fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net)); 14858c2ecf20Sopenharmony_ci fib6_start_gc(info->nl_net, rt); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ciout: 14898c2ecf20Sopenharmony_ci if (err) { 14908c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 14918c2ecf20Sopenharmony_ci /* 14928c2ecf20Sopenharmony_ci * If fib6_add_1 has cleared the old leaf pointer in the 14938c2ecf20Sopenharmony_ci * super-tree leaf node we have to find a new one for it. 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_ci if (pn != fn) { 14968c2ecf20Sopenharmony_ci struct fib6_info *pn_leaf = 14978c2ecf20Sopenharmony_ci rcu_dereference_protected(pn->leaf, 14988c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 14998c2ecf20Sopenharmony_ci if (pn_leaf == rt) { 15008c2ecf20Sopenharmony_ci pn_leaf = NULL; 15018c2ecf20Sopenharmony_ci RCU_INIT_POINTER(pn->leaf, NULL); 15028c2ecf20Sopenharmony_ci fib6_info_release(rt); 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci if (!pn_leaf && !(pn->fn_flags & RTN_RTINFO)) { 15058c2ecf20Sopenharmony_ci pn_leaf = fib6_find_prefix(info->nl_net, table, 15068c2ecf20Sopenharmony_ci pn); 15078c2ecf20Sopenharmony_ci if (!pn_leaf) 15088c2ecf20Sopenharmony_ci pn_leaf = 15098c2ecf20Sopenharmony_ci info->nl_net->ipv6.fib6_null_entry; 15108c2ecf20Sopenharmony_ci fib6_info_hold(pn_leaf); 15118c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->leaf, pn_leaf); 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci#endif 15158c2ecf20Sopenharmony_ci goto failure; 15168c2ecf20Sopenharmony_ci } else if (fib6_requires_src(rt)) { 15178c2ecf20Sopenharmony_ci fib6_routes_require_src_inc(info->nl_net); 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci return err; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cifailure: 15228c2ecf20Sopenharmony_ci /* fn->leaf could be NULL and fib6_repair_tree() needs to be called if: 15238c2ecf20Sopenharmony_ci * 1. fn is an intermediate node and we failed to add the new 15248c2ecf20Sopenharmony_ci * route to it in both subtree creation failure and fib6_add_rt2node() 15258c2ecf20Sopenharmony_ci * failure case. 15268c2ecf20Sopenharmony_ci * 2. fn is the root node in the table and we fail to add the first 15278c2ecf20Sopenharmony_ci * default route to it. 15288c2ecf20Sopenharmony_ci */ 15298c2ecf20Sopenharmony_ci if (fn && 15308c2ecf20Sopenharmony_ci (!(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)) || 15318c2ecf20Sopenharmony_ci (fn->fn_flags & RTN_TL_ROOT && 15328c2ecf20Sopenharmony_ci !rcu_access_pointer(fn->leaf)))) 15338c2ecf20Sopenharmony_ci fib6_repair_tree(info->nl_net, table, fn); 15348c2ecf20Sopenharmony_ci return err; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci/* 15388c2ecf20Sopenharmony_ci * Routing tree lookup 15398c2ecf20Sopenharmony_ci * 15408c2ecf20Sopenharmony_ci */ 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistruct lookup_args { 15438c2ecf20Sopenharmony_ci int offset; /* key offset on fib6_info */ 15448c2ecf20Sopenharmony_ci const struct in6_addr *addr; /* search key */ 15458c2ecf20Sopenharmony_ci}; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_node_lookup_1(struct fib6_node *root, 15488c2ecf20Sopenharmony_ci struct lookup_args *args) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct fib6_node *fn; 15518c2ecf20Sopenharmony_ci __be32 dir; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (unlikely(args->offset == 0)) 15548c2ecf20Sopenharmony_ci return NULL; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* 15578c2ecf20Sopenharmony_ci * Descend on a tree 15588c2ecf20Sopenharmony_ci */ 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci fn = root; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci for (;;) { 15638c2ecf20Sopenharmony_ci struct fib6_node *next; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci dir = addr_bit_set(args->addr, fn->fn_bit); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci next = dir ? rcu_dereference(fn->right) : 15688c2ecf20Sopenharmony_ci rcu_dereference(fn->left); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (next) { 15718c2ecf20Sopenharmony_ci fn = next; 15728c2ecf20Sopenharmony_ci continue; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci break; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci while (fn) { 15788c2ecf20Sopenharmony_ci struct fib6_node *subtree = FIB6_SUBTREE(fn); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (subtree || fn->fn_flags & RTN_RTINFO) { 15818c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference(fn->leaf); 15828c2ecf20Sopenharmony_ci struct rt6key *key; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (!leaf) 15858c2ecf20Sopenharmony_ci goto backtrack; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci key = (struct rt6key *) ((u8 *)leaf + args->offset); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { 15908c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 15918c2ecf20Sopenharmony_ci if (subtree) { 15928c2ecf20Sopenharmony_ci struct fib6_node *sfn; 15938c2ecf20Sopenharmony_ci sfn = fib6_node_lookup_1(subtree, 15948c2ecf20Sopenharmony_ci args + 1); 15958c2ecf20Sopenharmony_ci if (!sfn) 15968c2ecf20Sopenharmony_ci goto backtrack; 15978c2ecf20Sopenharmony_ci fn = sfn; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci#endif 16008c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_RTINFO) 16018c2ecf20Sopenharmony_ci return fn; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_cibacktrack: 16058c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_ROOT) 16068c2ecf20Sopenharmony_ci break; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci fn = rcu_dereference(fn->parent); 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci return NULL; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci/* called with rcu_read_lock() held 16158c2ecf20Sopenharmony_ci */ 16168c2ecf20Sopenharmony_cistruct fib6_node *fib6_node_lookup(struct fib6_node *root, 16178c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 16188c2ecf20Sopenharmony_ci const struct in6_addr *saddr) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci struct fib6_node *fn; 16218c2ecf20Sopenharmony_ci struct lookup_args args[] = { 16228c2ecf20Sopenharmony_ci { 16238c2ecf20Sopenharmony_ci .offset = offsetof(struct fib6_info, fib6_dst), 16248c2ecf20Sopenharmony_ci .addr = daddr, 16258c2ecf20Sopenharmony_ci }, 16268c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 16278c2ecf20Sopenharmony_ci { 16288c2ecf20Sopenharmony_ci .offset = offsetof(struct fib6_info, fib6_src), 16298c2ecf20Sopenharmony_ci .addr = saddr, 16308c2ecf20Sopenharmony_ci }, 16318c2ecf20Sopenharmony_ci#endif 16328c2ecf20Sopenharmony_ci { 16338c2ecf20Sopenharmony_ci .offset = 0, /* sentinel */ 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci }; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci fn = fib6_node_lookup_1(root, daddr ? args : args + 1); 16388c2ecf20Sopenharmony_ci if (!fn || fn->fn_flags & RTN_TL_ROOT) 16398c2ecf20Sopenharmony_ci fn = root; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci return fn; 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci/* 16458c2ecf20Sopenharmony_ci * Get node with specified destination prefix (and source prefix, 16468c2ecf20Sopenharmony_ci * if subtrees are used) 16478c2ecf20Sopenharmony_ci * exact_match == true means we try to find fn with exact match of 16488c2ecf20Sopenharmony_ci * the passed in prefix addr 16498c2ecf20Sopenharmony_ci * exact_match == false means we try to find fn with longest prefix 16508c2ecf20Sopenharmony_ci * match of the passed in prefix addr. This is useful for finding fn 16518c2ecf20Sopenharmony_ci * for cached route as it will be stored in the exception table under 16528c2ecf20Sopenharmony_ci * the node with longest prefix length. 16538c2ecf20Sopenharmony_ci */ 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_locate_1(struct fib6_node *root, 16578c2ecf20Sopenharmony_ci const struct in6_addr *addr, 16588c2ecf20Sopenharmony_ci int plen, int offset, 16598c2ecf20Sopenharmony_ci bool exact_match) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct fib6_node *fn, *prev = NULL; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci for (fn = root; fn ; ) { 16648c2ecf20Sopenharmony_ci struct fib6_info *leaf = rcu_dereference(fn->leaf); 16658c2ecf20Sopenharmony_ci struct rt6key *key; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* This node is being deleted */ 16688c2ecf20Sopenharmony_ci if (!leaf) { 16698c2ecf20Sopenharmony_ci if (plen <= fn->fn_bit) 16708c2ecf20Sopenharmony_ci goto out; 16718c2ecf20Sopenharmony_ci else 16728c2ecf20Sopenharmony_ci goto next; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci key = (struct rt6key *)((u8 *)leaf + offset); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* 16788c2ecf20Sopenharmony_ci * Prefix match 16798c2ecf20Sopenharmony_ci */ 16808c2ecf20Sopenharmony_ci if (plen < fn->fn_bit || 16818c2ecf20Sopenharmony_ci !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) 16828c2ecf20Sopenharmony_ci goto out; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci if (plen == fn->fn_bit) 16858c2ecf20Sopenharmony_ci return fn; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_RTINFO) 16888c2ecf20Sopenharmony_ci prev = fn; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cinext: 16918c2ecf20Sopenharmony_ci /* 16928c2ecf20Sopenharmony_ci * We have more bits to go 16938c2ecf20Sopenharmony_ci */ 16948c2ecf20Sopenharmony_ci if (addr_bit_set(addr, fn->fn_bit)) 16958c2ecf20Sopenharmony_ci fn = rcu_dereference(fn->right); 16968c2ecf20Sopenharmony_ci else 16978c2ecf20Sopenharmony_ci fn = rcu_dereference(fn->left); 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ciout: 17008c2ecf20Sopenharmony_ci if (exact_match) 17018c2ecf20Sopenharmony_ci return NULL; 17028c2ecf20Sopenharmony_ci else 17038c2ecf20Sopenharmony_ci return prev; 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistruct fib6_node *fib6_locate(struct fib6_node *root, 17078c2ecf20Sopenharmony_ci const struct in6_addr *daddr, int dst_len, 17088c2ecf20Sopenharmony_ci const struct in6_addr *saddr, int src_len, 17098c2ecf20Sopenharmony_ci bool exact_match) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci struct fib6_node *fn; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci fn = fib6_locate_1(root, daddr, dst_len, 17148c2ecf20Sopenharmony_ci offsetof(struct fib6_info, fib6_dst), 17158c2ecf20Sopenharmony_ci exact_match); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 17188c2ecf20Sopenharmony_ci if (src_len) { 17198c2ecf20Sopenharmony_ci WARN_ON(saddr == NULL); 17208c2ecf20Sopenharmony_ci if (fn) { 17218c2ecf20Sopenharmony_ci struct fib6_node *subtree = FIB6_SUBTREE(fn); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (subtree) { 17248c2ecf20Sopenharmony_ci fn = fib6_locate_1(subtree, saddr, src_len, 17258c2ecf20Sopenharmony_ci offsetof(struct fib6_info, fib6_src), 17268c2ecf20Sopenharmony_ci exact_match); 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci#endif 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci if (fn && fn->fn_flags & RTN_RTINFO) 17338c2ecf20Sopenharmony_ci return fn; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci return NULL; 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci/* 17408c2ecf20Sopenharmony_ci * Deletion 17418c2ecf20Sopenharmony_ci * 17428c2ecf20Sopenharmony_ci */ 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net, 17458c2ecf20Sopenharmony_ci struct fib6_table *table, 17468c2ecf20Sopenharmony_ci struct fib6_node *fn) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci struct fib6_node *child_left, *child_right; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_ROOT) 17518c2ecf20Sopenharmony_ci return net->ipv6.fib6_null_entry; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci while (fn) { 17548c2ecf20Sopenharmony_ci child_left = rcu_dereference_protected(fn->left, 17558c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17568c2ecf20Sopenharmony_ci child_right = rcu_dereference_protected(fn->right, 17578c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17588c2ecf20Sopenharmony_ci if (child_left) 17598c2ecf20Sopenharmony_ci return rcu_dereference_protected(child_left->leaf, 17608c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17618c2ecf20Sopenharmony_ci if (child_right) 17628c2ecf20Sopenharmony_ci return rcu_dereference_protected(child_right->leaf, 17638c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci fn = FIB6_SUBTREE(fn); 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci return NULL; 17688c2ecf20Sopenharmony_ci} 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci/* 17718c2ecf20Sopenharmony_ci * Called to trim the tree of intermediate nodes when possible. "fn" 17728c2ecf20Sopenharmony_ci * is the node we want to try and remove. 17738c2ecf20Sopenharmony_ci * Need to own table->tb6_lock 17748c2ecf20Sopenharmony_ci */ 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net, 17778c2ecf20Sopenharmony_ci struct fib6_table *table, 17788c2ecf20Sopenharmony_ci struct fib6_node *fn) 17798c2ecf20Sopenharmony_ci{ 17808c2ecf20Sopenharmony_ci int children; 17818c2ecf20Sopenharmony_ci int nstate; 17828c2ecf20Sopenharmony_ci struct fib6_node *child; 17838c2ecf20Sopenharmony_ci struct fib6_walker *w; 17848c2ecf20Sopenharmony_ci int iter = 0; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* Set fn->leaf to null_entry for root node. */ 17878c2ecf20Sopenharmony_ci if (fn->fn_flags & RTN_TL_ROOT) { 17888c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->leaf, net->ipv6.fib6_null_entry); 17898c2ecf20Sopenharmony_ci return fn; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci for (;;) { 17938c2ecf20Sopenharmony_ci struct fib6_node *fn_r = rcu_dereference_protected(fn->right, 17948c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17958c2ecf20Sopenharmony_ci struct fib6_node *fn_l = rcu_dereference_protected(fn->left, 17968c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17978c2ecf20Sopenharmony_ci struct fib6_node *pn = rcu_dereference_protected(fn->parent, 17988c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 17998c2ecf20Sopenharmony_ci struct fib6_node *pn_r = rcu_dereference_protected(pn->right, 18008c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 18018c2ecf20Sopenharmony_ci struct fib6_node *pn_l = rcu_dereference_protected(pn->left, 18028c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 18038c2ecf20Sopenharmony_ci struct fib6_info *fn_leaf = rcu_dereference_protected(fn->leaf, 18048c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 18058c2ecf20Sopenharmony_ci struct fib6_info *pn_leaf = rcu_dereference_protected(pn->leaf, 18068c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 18078c2ecf20Sopenharmony_ci struct fib6_info *new_fn_leaf; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter); 18108c2ecf20Sopenharmony_ci iter++; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci WARN_ON(fn->fn_flags & RTN_RTINFO); 18138c2ecf20Sopenharmony_ci WARN_ON(fn->fn_flags & RTN_TL_ROOT); 18148c2ecf20Sopenharmony_ci WARN_ON(fn_leaf); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci children = 0; 18178c2ecf20Sopenharmony_ci child = NULL; 18188c2ecf20Sopenharmony_ci if (fn_r) { 18198c2ecf20Sopenharmony_ci child = fn_r; 18208c2ecf20Sopenharmony_ci children |= 1; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci if (fn_l) { 18238c2ecf20Sopenharmony_ci child = fn_l; 18248c2ecf20Sopenharmony_ci children |= 2; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (children == 3 || FIB6_SUBTREE(fn) 18288c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 18298c2ecf20Sopenharmony_ci /* Subtree root (i.e. fn) may have one child */ 18308c2ecf20Sopenharmony_ci || (children && fn->fn_flags & RTN_ROOT) 18318c2ecf20Sopenharmony_ci#endif 18328c2ecf20Sopenharmony_ci ) { 18338c2ecf20Sopenharmony_ci new_fn_leaf = fib6_find_prefix(net, table, fn); 18348c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2 18358c2ecf20Sopenharmony_ci if (!new_fn_leaf) { 18368c2ecf20Sopenharmony_ci WARN_ON(!new_fn_leaf); 18378c2ecf20Sopenharmony_ci new_fn_leaf = net->ipv6.fib6_null_entry; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci#endif 18408c2ecf20Sopenharmony_ci fib6_info_hold(new_fn_leaf); 18418c2ecf20Sopenharmony_ci rcu_assign_pointer(fn->leaf, new_fn_leaf); 18428c2ecf20Sopenharmony_ci return pn; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 18468c2ecf20Sopenharmony_ci if (FIB6_SUBTREE(pn) == fn) { 18478c2ecf20Sopenharmony_ci WARN_ON(!(fn->fn_flags & RTN_ROOT)); 18488c2ecf20Sopenharmony_ci RCU_INIT_POINTER(pn->subtree, NULL); 18498c2ecf20Sopenharmony_ci nstate = FWS_L; 18508c2ecf20Sopenharmony_ci } else { 18518c2ecf20Sopenharmony_ci WARN_ON(fn->fn_flags & RTN_ROOT); 18528c2ecf20Sopenharmony_ci#endif 18538c2ecf20Sopenharmony_ci if (pn_r == fn) 18548c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->right, child); 18558c2ecf20Sopenharmony_ci else if (pn_l == fn) 18568c2ecf20Sopenharmony_ci rcu_assign_pointer(pn->left, child); 18578c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2 18588c2ecf20Sopenharmony_ci else 18598c2ecf20Sopenharmony_ci WARN_ON(1); 18608c2ecf20Sopenharmony_ci#endif 18618c2ecf20Sopenharmony_ci if (child) 18628c2ecf20Sopenharmony_ci rcu_assign_pointer(child->parent, pn); 18638c2ecf20Sopenharmony_ci nstate = FWS_R; 18648c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci#endif 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci read_lock(&net->ipv6.fib6_walker_lock); 18698c2ecf20Sopenharmony_ci FOR_WALKERS(net, w) { 18708c2ecf20Sopenharmony_ci if (!child) { 18718c2ecf20Sopenharmony_ci if (w->node == fn) { 18728c2ecf20Sopenharmony_ci RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate); 18738c2ecf20Sopenharmony_ci w->node = pn; 18748c2ecf20Sopenharmony_ci w->state = nstate; 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci } else { 18778c2ecf20Sopenharmony_ci if (w->node == fn) { 18788c2ecf20Sopenharmony_ci w->node = child; 18798c2ecf20Sopenharmony_ci if (children&2) { 18808c2ecf20Sopenharmony_ci RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); 18818c2ecf20Sopenharmony_ci w->state = w->state >= FWS_R ? FWS_U : FWS_INIT; 18828c2ecf20Sopenharmony_ci } else { 18838c2ecf20Sopenharmony_ci RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); 18848c2ecf20Sopenharmony_ci w->state = w->state >= FWS_C ? FWS_U : FWS_INIT; 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci read_unlock(&net->ipv6.fib6_walker_lock); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci node_free(net, fn); 18928c2ecf20Sopenharmony_ci if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn)) 18938c2ecf20Sopenharmony_ci return pn; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci RCU_INIT_POINTER(pn->leaf, NULL); 18968c2ecf20Sopenharmony_ci fib6_info_release(pn_leaf); 18978c2ecf20Sopenharmony_ci fn = pn; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci} 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_cistatic void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, 19028c2ecf20Sopenharmony_ci struct fib6_info __rcu **rtp, struct nl_info *info) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci struct fib6_info *leaf, *replace_rt = NULL; 19058c2ecf20Sopenharmony_ci struct fib6_walker *w; 19068c2ecf20Sopenharmony_ci struct fib6_info *rt = rcu_dereference_protected(*rtp, 19078c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 19088c2ecf20Sopenharmony_ci struct net *net = info->nl_net; 19098c2ecf20Sopenharmony_ci bool notify_del = false; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci RT6_TRACE("fib6_del_route\n"); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci /* If the deleted route is the first in the node and it is not part of 19148c2ecf20Sopenharmony_ci * a multipath route, then we need to replace it with the next route 19158c2ecf20Sopenharmony_ci * in the node, if exists. 19168c2ecf20Sopenharmony_ci */ 19178c2ecf20Sopenharmony_ci leaf = rcu_dereference_protected(fn->leaf, 19188c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 19198c2ecf20Sopenharmony_ci if (leaf == rt && !rt->fib6_nsiblings) { 19208c2ecf20Sopenharmony_ci if (rcu_access_pointer(rt->fib6_next)) 19218c2ecf20Sopenharmony_ci replace_rt = rcu_dereference_protected(rt->fib6_next, 19228c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 19238c2ecf20Sopenharmony_ci else 19248c2ecf20Sopenharmony_ci notify_del = true; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci /* Unlink it */ 19288c2ecf20Sopenharmony_ci *rtp = rt->fib6_next; 19298c2ecf20Sopenharmony_ci rt->fib6_node = NULL; 19308c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_rt_entries--; 19318c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_discarded_routes++; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* Reset round-robin state, if necessary */ 19348c2ecf20Sopenharmony_ci if (rcu_access_pointer(fn->rr_ptr) == rt) 19358c2ecf20Sopenharmony_ci fn->rr_ptr = NULL; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci /* Remove this entry from other siblings */ 19388c2ecf20Sopenharmony_ci if (rt->fib6_nsiblings) { 19398c2ecf20Sopenharmony_ci struct fib6_info *sibling, *next_sibling; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* The route is deleted from a multipath route. If this 19428c2ecf20Sopenharmony_ci * multipath route is the first route in the node, then we need 19438c2ecf20Sopenharmony_ci * to emit a delete notification. Otherwise, we need to skip 19448c2ecf20Sopenharmony_ci * the notification. 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_ci if (rt->fib6_metric == leaf->fib6_metric && 19478c2ecf20Sopenharmony_ci rt6_qualify_for_ecmp(leaf)) 19488c2ecf20Sopenharmony_ci notify_del = true; 19498c2ecf20Sopenharmony_ci list_for_each_entry_safe(sibling, next_sibling, 19508c2ecf20Sopenharmony_ci &rt->fib6_siblings, fib6_siblings) 19518c2ecf20Sopenharmony_ci sibling->fib6_nsiblings--; 19528c2ecf20Sopenharmony_ci rt->fib6_nsiblings = 0; 19538c2ecf20Sopenharmony_ci list_del_init(&rt->fib6_siblings); 19548c2ecf20Sopenharmony_ci rt6_multipath_rebalance(next_sibling); 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* Adjust walkers */ 19588c2ecf20Sopenharmony_ci read_lock(&net->ipv6.fib6_walker_lock); 19598c2ecf20Sopenharmony_ci FOR_WALKERS(net, w) { 19608c2ecf20Sopenharmony_ci if (w->state == FWS_C && w->leaf == rt) { 19618c2ecf20Sopenharmony_ci RT6_TRACE("walker %p adjusted by delroute\n", w); 19628c2ecf20Sopenharmony_ci w->leaf = rcu_dereference_protected(rt->fib6_next, 19638c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 19648c2ecf20Sopenharmony_ci if (!w->leaf) 19658c2ecf20Sopenharmony_ci w->state = FWS_U; 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci read_unlock(&net->ipv6.fib6_walker_lock); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* If it was last route, call fib6_repair_tree() to: 19718c2ecf20Sopenharmony_ci * 1. For root node, put back null_entry as how the table was created. 19728c2ecf20Sopenharmony_ci * 2. For other nodes, expunge its radix tree node. 19738c2ecf20Sopenharmony_ci */ 19748c2ecf20Sopenharmony_ci if (!rcu_access_pointer(fn->leaf)) { 19758c2ecf20Sopenharmony_ci if (!(fn->fn_flags & RTN_TL_ROOT)) { 19768c2ecf20Sopenharmony_ci fn->fn_flags &= ~RTN_RTINFO; 19778c2ecf20Sopenharmony_ci net->ipv6.rt6_stats->fib_route_nodes--; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci fn = fib6_repair_tree(net, table, fn); 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci fib6_purge_rt(rt, fn, net); 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci if (!info->skip_notify_kernel) { 19858c2ecf20Sopenharmony_ci if (notify_del) 19868c2ecf20Sopenharmony_ci call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, 19878c2ecf20Sopenharmony_ci rt, NULL); 19888c2ecf20Sopenharmony_ci else if (replace_rt) 19898c2ecf20Sopenharmony_ci call_fib6_entry_notifiers_replace(net, replace_rt); 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci if (!info->skip_notify) 19928c2ecf20Sopenharmony_ci inet6_rt_notify(RTM_DELROUTE, rt, info, 0); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci fib6_info_release(rt); 19958c2ecf20Sopenharmony_ci} 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci/* Need to own table->tb6_lock */ 19988c2ecf20Sopenharmony_ciint fib6_del(struct fib6_info *rt, struct nl_info *info) 19998c2ecf20Sopenharmony_ci{ 20008c2ecf20Sopenharmony_ci struct net *net = info->nl_net; 20018c2ecf20Sopenharmony_ci struct fib6_info __rcu **rtp; 20028c2ecf20Sopenharmony_ci struct fib6_info __rcu **rtp_next; 20038c2ecf20Sopenharmony_ci struct fib6_table *table; 20048c2ecf20Sopenharmony_ci struct fib6_node *fn; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci if (rt == net->ipv6.fib6_null_entry) 20078c2ecf20Sopenharmony_ci return -ENOENT; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci table = rt->fib6_table; 20108c2ecf20Sopenharmony_ci fn = rcu_dereference_protected(rt->fib6_node, 20118c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 20128c2ecf20Sopenharmony_ci if (!fn) 20138c2ecf20Sopenharmony_ci return -ENOENT; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci WARN_ON(!(fn->fn_flags & RTN_RTINFO)); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci /* 20188c2ecf20Sopenharmony_ci * Walk the leaf entries looking for ourself 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci for (rtp = &fn->leaf; *rtp; rtp = rtp_next) { 20228c2ecf20Sopenharmony_ci struct fib6_info *cur = rcu_dereference_protected(*rtp, 20238c2ecf20Sopenharmony_ci lockdep_is_held(&table->tb6_lock)); 20248c2ecf20Sopenharmony_ci if (rt == cur) { 20258c2ecf20Sopenharmony_ci if (fib6_requires_src(cur)) 20268c2ecf20Sopenharmony_ci fib6_routes_require_src_dec(info->nl_net); 20278c2ecf20Sopenharmony_ci fib6_del_route(table, fn, rtp, info); 20288c2ecf20Sopenharmony_ci return 0; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci rtp_next = &cur->fib6_next; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci return -ENOENT; 20338c2ecf20Sopenharmony_ci} 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci/* 20368c2ecf20Sopenharmony_ci * Tree traversal function. 20378c2ecf20Sopenharmony_ci * 20388c2ecf20Sopenharmony_ci * Certainly, it is not interrupt safe. 20398c2ecf20Sopenharmony_ci * However, it is internally reenterable wrt itself and fib6_add/fib6_del. 20408c2ecf20Sopenharmony_ci * It means, that we can modify tree during walking 20418c2ecf20Sopenharmony_ci * and use this function for garbage collection, clone pruning, 20428c2ecf20Sopenharmony_ci * cleaning tree when a device goes down etc. etc. 20438c2ecf20Sopenharmony_ci * 20448c2ecf20Sopenharmony_ci * It guarantees that every node will be traversed, 20458c2ecf20Sopenharmony_ci * and that it will be traversed only once. 20468c2ecf20Sopenharmony_ci * 20478c2ecf20Sopenharmony_ci * Callback function w->func may return: 20488c2ecf20Sopenharmony_ci * 0 -> continue walking. 20498c2ecf20Sopenharmony_ci * positive value -> walking is suspended (used by tree dumps, 20508c2ecf20Sopenharmony_ci * and probably by gc, if it will be split to several slices) 20518c2ecf20Sopenharmony_ci * negative value -> terminate walking. 20528c2ecf20Sopenharmony_ci * 20538c2ecf20Sopenharmony_ci * The function itself returns: 20548c2ecf20Sopenharmony_ci * 0 -> walk is complete. 20558c2ecf20Sopenharmony_ci * >0 -> walk is incomplete (i.e. suspended) 20568c2ecf20Sopenharmony_ci * <0 -> walk is terminated by an error. 20578c2ecf20Sopenharmony_ci * 20588c2ecf20Sopenharmony_ci * This function is called with tb6_lock held. 20598c2ecf20Sopenharmony_ci */ 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w) 20628c2ecf20Sopenharmony_ci{ 20638c2ecf20Sopenharmony_ci struct fib6_node *fn, *pn, *left, *right; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci /* w->root should always be table->tb6_root */ 20668c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT)); 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci for (;;) { 20698c2ecf20Sopenharmony_ci fn = w->node; 20708c2ecf20Sopenharmony_ci if (!fn) 20718c2ecf20Sopenharmony_ci return 0; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci switch (w->state) { 20748c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 20758c2ecf20Sopenharmony_ci case FWS_S: 20768c2ecf20Sopenharmony_ci if (FIB6_SUBTREE(fn)) { 20778c2ecf20Sopenharmony_ci w->node = FIB6_SUBTREE(fn); 20788c2ecf20Sopenharmony_ci continue; 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci w->state = FWS_L; 20818c2ecf20Sopenharmony_ci fallthrough; 20828c2ecf20Sopenharmony_ci#endif 20838c2ecf20Sopenharmony_ci case FWS_L: 20848c2ecf20Sopenharmony_ci left = rcu_dereference_protected(fn->left, 1); 20858c2ecf20Sopenharmony_ci if (left) { 20868c2ecf20Sopenharmony_ci w->node = left; 20878c2ecf20Sopenharmony_ci w->state = FWS_INIT; 20888c2ecf20Sopenharmony_ci continue; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci w->state = FWS_R; 20918c2ecf20Sopenharmony_ci fallthrough; 20928c2ecf20Sopenharmony_ci case FWS_R: 20938c2ecf20Sopenharmony_ci right = rcu_dereference_protected(fn->right, 1); 20948c2ecf20Sopenharmony_ci if (right) { 20958c2ecf20Sopenharmony_ci w->node = right; 20968c2ecf20Sopenharmony_ci w->state = FWS_INIT; 20978c2ecf20Sopenharmony_ci continue; 20988c2ecf20Sopenharmony_ci } 20998c2ecf20Sopenharmony_ci w->state = FWS_C; 21008c2ecf20Sopenharmony_ci w->leaf = rcu_dereference_protected(fn->leaf, 1); 21018c2ecf20Sopenharmony_ci fallthrough; 21028c2ecf20Sopenharmony_ci case FWS_C: 21038c2ecf20Sopenharmony_ci if (w->leaf && fn->fn_flags & RTN_RTINFO) { 21048c2ecf20Sopenharmony_ci int err; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci if (w->skip) { 21078c2ecf20Sopenharmony_ci w->skip--; 21088c2ecf20Sopenharmony_ci goto skip; 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci err = w->func(w); 21128c2ecf20Sopenharmony_ci if (err) 21138c2ecf20Sopenharmony_ci return err; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci w->count++; 21168c2ecf20Sopenharmony_ci continue; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ciskip: 21198c2ecf20Sopenharmony_ci w->state = FWS_U; 21208c2ecf20Sopenharmony_ci fallthrough; 21218c2ecf20Sopenharmony_ci case FWS_U: 21228c2ecf20Sopenharmony_ci if (fn == w->root) 21238c2ecf20Sopenharmony_ci return 0; 21248c2ecf20Sopenharmony_ci pn = rcu_dereference_protected(fn->parent, 1); 21258c2ecf20Sopenharmony_ci left = rcu_dereference_protected(pn->left, 1); 21268c2ecf20Sopenharmony_ci right = rcu_dereference_protected(pn->right, 1); 21278c2ecf20Sopenharmony_ci w->node = pn; 21288c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 21298c2ecf20Sopenharmony_ci if (FIB6_SUBTREE(pn) == fn) { 21308c2ecf20Sopenharmony_ci WARN_ON(!(fn->fn_flags & RTN_ROOT)); 21318c2ecf20Sopenharmony_ci w->state = FWS_L; 21328c2ecf20Sopenharmony_ci continue; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci#endif 21358c2ecf20Sopenharmony_ci if (left == fn) { 21368c2ecf20Sopenharmony_ci w->state = FWS_R; 21378c2ecf20Sopenharmony_ci continue; 21388c2ecf20Sopenharmony_ci } 21398c2ecf20Sopenharmony_ci if (right == fn) { 21408c2ecf20Sopenharmony_ci w->state = FWS_C; 21418c2ecf20Sopenharmony_ci w->leaf = rcu_dereference_protected(w->node->leaf, 1); 21428c2ecf20Sopenharmony_ci continue; 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2 21458c2ecf20Sopenharmony_ci WARN_ON(1); 21468c2ecf20Sopenharmony_ci#endif 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci } 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w) 21528c2ecf20Sopenharmony_ci{ 21538c2ecf20Sopenharmony_ci int res; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci w->state = FWS_INIT; 21568c2ecf20Sopenharmony_ci w->node = w->root; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci fib6_walker_link(net, w); 21598c2ecf20Sopenharmony_ci res = fib6_walk_continue(w); 21608c2ecf20Sopenharmony_ci if (res <= 0) 21618c2ecf20Sopenharmony_ci fib6_walker_unlink(net, w); 21628c2ecf20Sopenharmony_ci return res; 21638c2ecf20Sopenharmony_ci} 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_cistatic int fib6_clean_node(struct fib6_walker *w) 21668c2ecf20Sopenharmony_ci{ 21678c2ecf20Sopenharmony_ci int res; 21688c2ecf20Sopenharmony_ci struct fib6_info *rt; 21698c2ecf20Sopenharmony_ci struct fib6_cleaner *c = container_of(w, struct fib6_cleaner, w); 21708c2ecf20Sopenharmony_ci struct nl_info info = { 21718c2ecf20Sopenharmony_ci .nl_net = c->net, 21728c2ecf20Sopenharmony_ci .skip_notify = c->skip_notify, 21738c2ecf20Sopenharmony_ci }; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci if (c->sernum != FIB6_NO_SERNUM_CHANGE && 21768c2ecf20Sopenharmony_ci READ_ONCE(w->node->fn_sernum) != c->sernum) 21778c2ecf20Sopenharmony_ci WRITE_ONCE(w->node->fn_sernum, c->sernum); 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci if (!c->func) { 21808c2ecf20Sopenharmony_ci WARN_ON_ONCE(c->sernum == FIB6_NO_SERNUM_CHANGE); 21818c2ecf20Sopenharmony_ci w->leaf = NULL; 21828c2ecf20Sopenharmony_ci return 0; 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci for_each_fib6_walker_rt(w) { 21868c2ecf20Sopenharmony_ci res = c->func(rt, c->arg); 21878c2ecf20Sopenharmony_ci if (res == -1) { 21888c2ecf20Sopenharmony_ci w->leaf = rt; 21898c2ecf20Sopenharmony_ci res = fib6_del(rt, &info); 21908c2ecf20Sopenharmony_ci if (res) { 21918c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2 21928c2ecf20Sopenharmony_ci pr_debug("%s: del failed: rt=%p@%p err=%d\n", 21938c2ecf20Sopenharmony_ci __func__, rt, 21948c2ecf20Sopenharmony_ci rcu_access_pointer(rt->fib6_node), 21958c2ecf20Sopenharmony_ci res); 21968c2ecf20Sopenharmony_ci#endif 21978c2ecf20Sopenharmony_ci continue; 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci return 0; 22008c2ecf20Sopenharmony_ci } else if (res == -2) { 22018c2ecf20Sopenharmony_ci if (WARN_ON(!rt->fib6_nsiblings)) 22028c2ecf20Sopenharmony_ci continue; 22038c2ecf20Sopenharmony_ci rt = list_last_entry(&rt->fib6_siblings, 22048c2ecf20Sopenharmony_ci struct fib6_info, fib6_siblings); 22058c2ecf20Sopenharmony_ci continue; 22068c2ecf20Sopenharmony_ci } 22078c2ecf20Sopenharmony_ci WARN_ON(res != 0); 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci w->leaf = rt; 22108c2ecf20Sopenharmony_ci return 0; 22118c2ecf20Sopenharmony_ci} 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci/* 22148c2ecf20Sopenharmony_ci * Convenient frontend to tree walker. 22158c2ecf20Sopenharmony_ci * 22168c2ecf20Sopenharmony_ci * func is called on each route. 22178c2ecf20Sopenharmony_ci * It may return -2 -> skip multipath route. 22188c2ecf20Sopenharmony_ci * -1 -> delete this route. 22198c2ecf20Sopenharmony_ci * 0 -> continue walking 22208c2ecf20Sopenharmony_ci */ 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic void fib6_clean_tree(struct net *net, struct fib6_node *root, 22238c2ecf20Sopenharmony_ci int (*func)(struct fib6_info *, void *arg), 22248c2ecf20Sopenharmony_ci int sernum, void *arg, bool skip_notify) 22258c2ecf20Sopenharmony_ci{ 22268c2ecf20Sopenharmony_ci struct fib6_cleaner c; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci c.w.root = root; 22298c2ecf20Sopenharmony_ci c.w.func = fib6_clean_node; 22308c2ecf20Sopenharmony_ci c.w.count = 0; 22318c2ecf20Sopenharmony_ci c.w.skip = 0; 22328c2ecf20Sopenharmony_ci c.w.skip_in_node = 0; 22338c2ecf20Sopenharmony_ci c.func = func; 22348c2ecf20Sopenharmony_ci c.sernum = sernum; 22358c2ecf20Sopenharmony_ci c.arg = arg; 22368c2ecf20Sopenharmony_ci c.net = net; 22378c2ecf20Sopenharmony_ci c.skip_notify = skip_notify; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci fib6_walk(net, &c.w); 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_cistatic void __fib6_clean_all(struct net *net, 22438c2ecf20Sopenharmony_ci int (*func)(struct fib6_info *, void *), 22448c2ecf20Sopenharmony_ci int sernum, void *arg, bool skip_notify) 22458c2ecf20Sopenharmony_ci{ 22468c2ecf20Sopenharmony_ci struct fib6_table *table; 22478c2ecf20Sopenharmony_ci struct hlist_head *head; 22488c2ecf20Sopenharmony_ci unsigned int h; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci rcu_read_lock(); 22518c2ecf20Sopenharmony_ci for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 22528c2ecf20Sopenharmony_ci head = &net->ipv6.fib_table_hash[h]; 22538c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(table, head, tb6_hlist) { 22548c2ecf20Sopenharmony_ci spin_lock_bh(&table->tb6_lock); 22558c2ecf20Sopenharmony_ci fib6_clean_tree(net, &table->tb6_root, 22568c2ecf20Sopenharmony_ci func, sernum, arg, skip_notify); 22578c2ecf20Sopenharmony_ci spin_unlock_bh(&table->tb6_lock); 22588c2ecf20Sopenharmony_ci } 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci rcu_read_unlock(); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_civoid fib6_clean_all(struct net *net, int (*func)(struct fib6_info *, void *), 22648c2ecf20Sopenharmony_ci void *arg) 22658c2ecf20Sopenharmony_ci{ 22668c2ecf20Sopenharmony_ci __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, false); 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_civoid fib6_clean_all_skip_notify(struct net *net, 22708c2ecf20Sopenharmony_ci int (*func)(struct fib6_info *, void *), 22718c2ecf20Sopenharmony_ci void *arg) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, true); 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_cistatic void fib6_flush_trees(struct net *net) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci int new_sernum = fib6_new_sernum(net); 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci __fib6_clean_all(net, NULL, new_sernum, NULL, false); 22818c2ecf20Sopenharmony_ci} 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci/* 22848c2ecf20Sopenharmony_ci * Garbage collection 22858c2ecf20Sopenharmony_ci */ 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic int fib6_age(struct fib6_info *rt, void *arg) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct fib6_gc_args *gc_args = arg; 22908c2ecf20Sopenharmony_ci unsigned long now = jiffies; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* 22938c2ecf20Sopenharmony_ci * check addrconf expiration here. 22948c2ecf20Sopenharmony_ci * Routes are expired even if they are in use. 22958c2ecf20Sopenharmony_ci */ 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci if (rt->fib6_flags & RTF_EXPIRES && rt->expires) { 22988c2ecf20Sopenharmony_ci if (time_after(now, rt->expires)) { 22998c2ecf20Sopenharmony_ci RT6_TRACE("expiring %p\n", rt); 23008c2ecf20Sopenharmony_ci return -1; 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci gc_args->more++; 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci /* Also age clones in the exception table. 23068c2ecf20Sopenharmony_ci * Note, that clones are aged out 23078c2ecf20Sopenharmony_ci * only if they are not in use now. 23088c2ecf20Sopenharmony_ci */ 23098c2ecf20Sopenharmony_ci rt6_age_exceptions(rt, gc_args, now); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci return 0; 23128c2ecf20Sopenharmony_ci} 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_civoid fib6_run_gc(unsigned long expires, struct net *net, bool force) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci struct fib6_gc_args gc_args; 23178c2ecf20Sopenharmony_ci unsigned long now; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (force) { 23208c2ecf20Sopenharmony_ci spin_lock_bh(&net->ipv6.fib6_gc_lock); 23218c2ecf20Sopenharmony_ci } else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) { 23228c2ecf20Sopenharmony_ci mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ); 23238c2ecf20Sopenharmony_ci return; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci gc_args.timeout = expires ? (int)expires : 23268c2ecf20Sopenharmony_ci net->ipv6.sysctl.ip6_rt_gc_interval; 23278c2ecf20Sopenharmony_ci gc_args.more = 0; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci fib6_clean_all(net, fib6_age, &gc_args); 23308c2ecf20Sopenharmony_ci now = jiffies; 23318c2ecf20Sopenharmony_ci net->ipv6.ip6_rt_last_gc = now; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci if (gc_args.more) 23348c2ecf20Sopenharmony_ci mod_timer(&net->ipv6.ip6_fib_timer, 23358c2ecf20Sopenharmony_ci round_jiffies(now 23368c2ecf20Sopenharmony_ci + net->ipv6.sysctl.ip6_rt_gc_interval)); 23378c2ecf20Sopenharmony_ci else 23388c2ecf20Sopenharmony_ci del_timer(&net->ipv6.ip6_fib_timer); 23398c2ecf20Sopenharmony_ci spin_unlock_bh(&net->ipv6.fib6_gc_lock); 23408c2ecf20Sopenharmony_ci} 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t) 23438c2ecf20Sopenharmony_ci{ 23448c2ecf20Sopenharmony_ci struct net *arg = from_timer(arg, t, ipv6.ip6_fib_timer); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci fib6_run_gc(0, arg, true); 23478c2ecf20Sopenharmony_ci} 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_cistatic int __net_init fib6_net_init(struct net *net) 23508c2ecf20Sopenharmony_ci{ 23518c2ecf20Sopenharmony_ci size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ; 23528c2ecf20Sopenharmony_ci int err; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci err = fib6_notifier_init(net); 23558c2ecf20Sopenharmony_ci if (err) 23568c2ecf20Sopenharmony_ci return err; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci spin_lock_init(&net->ipv6.fib6_gc_lock); 23598c2ecf20Sopenharmony_ci rwlock_init(&net->ipv6.fib6_walker_lock); 23608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.fib6_walkers); 23618c2ecf20Sopenharmony_ci timer_setup(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, 0); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL); 23648c2ecf20Sopenharmony_ci if (!net->ipv6.rt6_stats) 23658c2ecf20Sopenharmony_ci goto out_timer; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci /* Avoid false sharing : Use at least a full cache line */ 23688c2ecf20Sopenharmony_ci size = max_t(size_t, size, L1_CACHE_BYTES); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci net->ipv6.fib_table_hash = kzalloc(size, GFP_KERNEL); 23718c2ecf20Sopenharmony_ci if (!net->ipv6.fib_table_hash) 23728c2ecf20Sopenharmony_ci goto out_rt6_stats; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl), 23758c2ecf20Sopenharmony_ci GFP_KERNEL); 23768c2ecf20Sopenharmony_ci if (!net->ipv6.fib6_main_tbl) 23778c2ecf20Sopenharmony_ci goto out_fib_table_hash; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN; 23808c2ecf20Sopenharmony_ci rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf, 23818c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry); 23828c2ecf20Sopenharmony_ci net->ipv6.fib6_main_tbl->tb6_root.fn_flags = 23838c2ecf20Sopenharmony_ci RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; 23848c2ecf20Sopenharmony_ci inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 23878c2ecf20Sopenharmony_ci net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl), 23888c2ecf20Sopenharmony_ci GFP_KERNEL); 23898c2ecf20Sopenharmony_ci if (!net->ipv6.fib6_local_tbl) 23908c2ecf20Sopenharmony_ci goto out_fib6_main_tbl; 23918c2ecf20Sopenharmony_ci net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL; 23928c2ecf20Sopenharmony_ci rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf, 23938c2ecf20Sopenharmony_ci net->ipv6.fib6_null_entry); 23948c2ecf20Sopenharmony_ci net->ipv6.fib6_local_tbl->tb6_root.fn_flags = 23958c2ecf20Sopenharmony_ci RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; 23968c2ecf20Sopenharmony_ci inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers); 23978c2ecf20Sopenharmony_ci#endif 23988c2ecf20Sopenharmony_ci fib6_tables_init(net); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci return 0; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES 24038c2ecf20Sopenharmony_ciout_fib6_main_tbl: 24048c2ecf20Sopenharmony_ci kfree(net->ipv6.fib6_main_tbl); 24058c2ecf20Sopenharmony_ci#endif 24068c2ecf20Sopenharmony_ciout_fib_table_hash: 24078c2ecf20Sopenharmony_ci kfree(net->ipv6.fib_table_hash); 24088c2ecf20Sopenharmony_ciout_rt6_stats: 24098c2ecf20Sopenharmony_ci kfree(net->ipv6.rt6_stats); 24108c2ecf20Sopenharmony_ciout_timer: 24118c2ecf20Sopenharmony_ci fib6_notifier_exit(net); 24128c2ecf20Sopenharmony_ci return -ENOMEM; 24138c2ecf20Sopenharmony_ci} 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic void fib6_net_exit(struct net *net) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci unsigned int i; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci del_timer_sync(&net->ipv6.ip6_fib_timer); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci for (i = 0; i < FIB6_TABLE_HASHSZ; i++) { 24228c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv6.fib_table_hash[i]; 24238c2ecf20Sopenharmony_ci struct hlist_node *tmp; 24248c2ecf20Sopenharmony_ci struct fib6_table *tb; 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) { 24278c2ecf20Sopenharmony_ci hlist_del(&tb->tb6_hlist); 24288c2ecf20Sopenharmony_ci fib6_free_table(tb); 24298c2ecf20Sopenharmony_ci } 24308c2ecf20Sopenharmony_ci } 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci kfree(net->ipv6.fib_table_hash); 24338c2ecf20Sopenharmony_ci kfree(net->ipv6.rt6_stats); 24348c2ecf20Sopenharmony_ci fib6_notifier_exit(net); 24358c2ecf20Sopenharmony_ci} 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_cistatic struct pernet_operations fib6_net_ops = { 24388c2ecf20Sopenharmony_ci .init = fib6_net_init, 24398c2ecf20Sopenharmony_ci .exit = fib6_net_exit, 24408c2ecf20Sopenharmony_ci}; 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ciint __init fib6_init(void) 24438c2ecf20Sopenharmony_ci{ 24448c2ecf20Sopenharmony_ci int ret = -ENOMEM; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci fib6_node_kmem = kmem_cache_create("fib6_nodes", 24478c2ecf20Sopenharmony_ci sizeof(struct fib6_node), 24488c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 24498c2ecf20Sopenharmony_ci NULL); 24508c2ecf20Sopenharmony_ci if (!fib6_node_kmem) 24518c2ecf20Sopenharmony_ci goto out; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci ret = register_pernet_subsys(&fib6_net_ops); 24548c2ecf20Sopenharmony_ci if (ret) 24558c2ecf20Sopenharmony_ci goto out_kmem_cache_create; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL, 24588c2ecf20Sopenharmony_ci inet6_dump_fib, 0); 24598c2ecf20Sopenharmony_ci if (ret) 24608c2ecf20Sopenharmony_ci goto out_unregister_subsys; 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci __fib6_flush_trees = fib6_flush_trees; 24638c2ecf20Sopenharmony_ciout: 24648c2ecf20Sopenharmony_ci return ret; 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ciout_unregister_subsys: 24678c2ecf20Sopenharmony_ci unregister_pernet_subsys(&fib6_net_ops); 24688c2ecf20Sopenharmony_ciout_kmem_cache_create: 24698c2ecf20Sopenharmony_ci kmem_cache_destroy(fib6_node_kmem); 24708c2ecf20Sopenharmony_ci goto out; 24718c2ecf20Sopenharmony_ci} 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_civoid fib6_gc_cleanup(void) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci unregister_pernet_subsys(&fib6_net_ops); 24768c2ecf20Sopenharmony_ci kmem_cache_destroy(fib6_node_kmem); 24778c2ecf20Sopenharmony_ci} 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 24808c2ecf20Sopenharmony_cistatic int ipv6_route_native_seq_show(struct seq_file *seq, void *v) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci struct fib6_info *rt = v; 24838c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = seq->private; 24848c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh = rt->fib6_nh; 24858c2ecf20Sopenharmony_ci unsigned int flags = rt->fib6_flags; 24868c2ecf20Sopenharmony_ci const struct net_device *dev; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci if (rt->nh) 24898c2ecf20Sopenharmony_ci fib6_nh = nexthop_fib6_nh_bh(rt->nh); 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES 24948c2ecf20Sopenharmony_ci seq_printf(seq, "%pi6 %02x ", &rt->fib6_src.addr, rt->fib6_src.plen); 24958c2ecf20Sopenharmony_ci#else 24968c2ecf20Sopenharmony_ci seq_puts(seq, "00000000000000000000000000000000 00 "); 24978c2ecf20Sopenharmony_ci#endif 24988c2ecf20Sopenharmony_ci if (fib6_nh->fib_nh_gw_family) { 24998c2ecf20Sopenharmony_ci flags |= RTF_GATEWAY; 25008c2ecf20Sopenharmony_ci seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6); 25018c2ecf20Sopenharmony_ci } else { 25028c2ecf20Sopenharmony_ci seq_puts(seq, "00000000000000000000000000000000"); 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci dev = fib6_nh->fib_nh_dev; 25068c2ecf20Sopenharmony_ci seq_printf(seq, " %08x %08x %08x %08x %8s\n", 25078c2ecf20Sopenharmony_ci rt->fib6_metric, refcount_read(&rt->fib6_ref), 0, 25088c2ecf20Sopenharmony_ci flags, dev ? dev->name : ""); 25098c2ecf20Sopenharmony_ci iter->w.leaf = NULL; 25108c2ecf20Sopenharmony_ci return 0; 25118c2ecf20Sopenharmony_ci} 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_cistatic int ipv6_route_yield(struct fib6_walker *w) 25148c2ecf20Sopenharmony_ci{ 25158c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = w->args; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (!iter->skip) 25188c2ecf20Sopenharmony_ci return 1; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci do { 25218c2ecf20Sopenharmony_ci iter->w.leaf = rcu_dereference_protected( 25228c2ecf20Sopenharmony_ci iter->w.leaf->fib6_next, 25238c2ecf20Sopenharmony_ci lockdep_is_held(&iter->tbl->tb6_lock)); 25248c2ecf20Sopenharmony_ci iter->skip--; 25258c2ecf20Sopenharmony_ci if (!iter->skip && iter->w.leaf) 25268c2ecf20Sopenharmony_ci return 1; 25278c2ecf20Sopenharmony_ci } while (iter->w.leaf); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci return 0; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_cistatic void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter, 25338c2ecf20Sopenharmony_ci struct net *net) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci memset(&iter->w, 0, sizeof(iter->w)); 25368c2ecf20Sopenharmony_ci iter->w.func = ipv6_route_yield; 25378c2ecf20Sopenharmony_ci iter->w.root = &iter->tbl->tb6_root; 25388c2ecf20Sopenharmony_ci iter->w.state = FWS_INIT; 25398c2ecf20Sopenharmony_ci iter->w.node = iter->w.root; 25408c2ecf20Sopenharmony_ci iter->w.args = iter; 25418c2ecf20Sopenharmony_ci iter->sernum = READ_ONCE(iter->w.root->fn_sernum); 25428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&iter->w.lh); 25438c2ecf20Sopenharmony_ci fib6_walker_link(net, &iter->w); 25448c2ecf20Sopenharmony_ci} 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_cistatic struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl, 25478c2ecf20Sopenharmony_ci struct net *net) 25488c2ecf20Sopenharmony_ci{ 25498c2ecf20Sopenharmony_ci unsigned int h; 25508c2ecf20Sopenharmony_ci struct hlist_node *node; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci if (tbl) { 25538c2ecf20Sopenharmony_ci h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1; 25548c2ecf20Sopenharmony_ci node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist)); 25558c2ecf20Sopenharmony_ci } else { 25568c2ecf20Sopenharmony_ci h = 0; 25578c2ecf20Sopenharmony_ci node = NULL; 25588c2ecf20Sopenharmony_ci } 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci while (!node && h < FIB6_TABLE_HASHSZ) { 25618c2ecf20Sopenharmony_ci node = rcu_dereference_bh( 25628c2ecf20Sopenharmony_ci hlist_first_rcu(&net->ipv6.fib_table_hash[h++])); 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci return hlist_entry_safe(node, struct fib6_table, tb6_hlist); 25658c2ecf20Sopenharmony_ci} 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_cistatic void ipv6_route_check_sernum(struct ipv6_route_iter *iter) 25688c2ecf20Sopenharmony_ci{ 25698c2ecf20Sopenharmony_ci int sernum = READ_ONCE(iter->w.root->fn_sernum); 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci if (iter->sernum != sernum) { 25728c2ecf20Sopenharmony_ci iter->sernum = sernum; 25738c2ecf20Sopenharmony_ci iter->w.state = FWS_INIT; 25748c2ecf20Sopenharmony_ci iter->w.node = iter->w.root; 25758c2ecf20Sopenharmony_ci WARN_ON(iter->w.skip); 25768c2ecf20Sopenharmony_ci iter->w.skip = iter->w.count; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_cistatic void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) 25818c2ecf20Sopenharmony_ci{ 25828c2ecf20Sopenharmony_ci int r; 25838c2ecf20Sopenharmony_ci struct fib6_info *n; 25848c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 25858c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = seq->private; 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci ++(*pos); 25888c2ecf20Sopenharmony_ci if (!v) 25898c2ecf20Sopenharmony_ci goto iter_table; 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci n = rcu_dereference_bh(((struct fib6_info *)v)->fib6_next); 25928c2ecf20Sopenharmony_ci if (n) 25938c2ecf20Sopenharmony_ci return n; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ciiter_table: 25968c2ecf20Sopenharmony_ci ipv6_route_check_sernum(iter); 25978c2ecf20Sopenharmony_ci spin_lock_bh(&iter->tbl->tb6_lock); 25988c2ecf20Sopenharmony_ci r = fib6_walk_continue(&iter->w); 25998c2ecf20Sopenharmony_ci spin_unlock_bh(&iter->tbl->tb6_lock); 26008c2ecf20Sopenharmony_ci if (r > 0) { 26018c2ecf20Sopenharmony_ci return iter->w.leaf; 26028c2ecf20Sopenharmony_ci } else if (r < 0) { 26038c2ecf20Sopenharmony_ci fib6_walker_unlink(net, &iter->w); 26048c2ecf20Sopenharmony_ci return NULL; 26058c2ecf20Sopenharmony_ci } 26068c2ecf20Sopenharmony_ci fib6_walker_unlink(net, &iter->w); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci iter->tbl = ipv6_route_seq_next_table(iter->tbl, net); 26098c2ecf20Sopenharmony_ci if (!iter->tbl) 26108c2ecf20Sopenharmony_ci return NULL; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci ipv6_route_seq_setup_walk(iter, net); 26138c2ecf20Sopenharmony_ci goto iter_table; 26148c2ecf20Sopenharmony_ci} 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_cistatic void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos) 26178c2ecf20Sopenharmony_ci __acquires(RCU_BH) 26188c2ecf20Sopenharmony_ci{ 26198c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 26208c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = seq->private; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 26238c2ecf20Sopenharmony_ci iter->tbl = ipv6_route_seq_next_table(NULL, net); 26248c2ecf20Sopenharmony_ci iter->skip = *pos; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci if (iter->tbl) { 26278c2ecf20Sopenharmony_ci loff_t p = 0; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci ipv6_route_seq_setup_walk(iter, net); 26308c2ecf20Sopenharmony_ci return ipv6_route_seq_next(seq, NULL, &p); 26318c2ecf20Sopenharmony_ci } else { 26328c2ecf20Sopenharmony_ci return NULL; 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci} 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_cistatic bool ipv6_route_iter_active(struct ipv6_route_iter *iter) 26378c2ecf20Sopenharmony_ci{ 26388c2ecf20Sopenharmony_ci struct fib6_walker *w = &iter->w; 26398c2ecf20Sopenharmony_ci return w->node && !(w->state == FWS_U && w->node == w->root); 26408c2ecf20Sopenharmony_ci} 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_cistatic void ipv6_route_native_seq_stop(struct seq_file *seq, void *v) 26438c2ecf20Sopenharmony_ci __releases(RCU_BH) 26448c2ecf20Sopenharmony_ci{ 26458c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 26468c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = seq->private; 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci if (ipv6_route_iter_active(iter)) 26498c2ecf20Sopenharmony_ci fib6_walker_unlink(net, &iter->w); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 26528c2ecf20Sopenharmony_ci} 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL) 26558c2ecf20Sopenharmony_cistatic int ipv6_route_prog_seq_show(struct bpf_prog *prog, 26568c2ecf20Sopenharmony_ci struct bpf_iter_meta *meta, 26578c2ecf20Sopenharmony_ci void *v) 26588c2ecf20Sopenharmony_ci{ 26598c2ecf20Sopenharmony_ci struct bpf_iter__ipv6_route ctx; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci ctx.meta = meta; 26628c2ecf20Sopenharmony_ci ctx.rt = v; 26638c2ecf20Sopenharmony_ci return bpf_iter_run_prog(prog, &ctx); 26648c2ecf20Sopenharmony_ci} 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v) 26678c2ecf20Sopenharmony_ci{ 26688c2ecf20Sopenharmony_ci struct ipv6_route_iter *iter = seq->private; 26698c2ecf20Sopenharmony_ci struct bpf_iter_meta meta; 26708c2ecf20Sopenharmony_ci struct bpf_prog *prog; 26718c2ecf20Sopenharmony_ci int ret; 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci meta.seq = seq; 26748c2ecf20Sopenharmony_ci prog = bpf_iter_get_info(&meta, false); 26758c2ecf20Sopenharmony_ci if (!prog) 26768c2ecf20Sopenharmony_ci return ipv6_route_native_seq_show(seq, v); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci ret = ipv6_route_prog_seq_show(prog, &meta, v); 26798c2ecf20Sopenharmony_ci iter->w.leaf = NULL; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci return ret; 26828c2ecf20Sopenharmony_ci} 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v) 26858c2ecf20Sopenharmony_ci{ 26868c2ecf20Sopenharmony_ci struct bpf_iter_meta meta; 26878c2ecf20Sopenharmony_ci struct bpf_prog *prog; 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci if (!v) { 26908c2ecf20Sopenharmony_ci meta.seq = seq; 26918c2ecf20Sopenharmony_ci prog = bpf_iter_get_info(&meta, true); 26928c2ecf20Sopenharmony_ci if (prog) 26938c2ecf20Sopenharmony_ci (void)ipv6_route_prog_seq_show(prog, &meta, v); 26948c2ecf20Sopenharmony_ci } 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci ipv6_route_native_seq_stop(seq, v); 26978c2ecf20Sopenharmony_ci} 26988c2ecf20Sopenharmony_ci#else 26998c2ecf20Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v) 27008c2ecf20Sopenharmony_ci{ 27018c2ecf20Sopenharmony_ci return ipv6_route_native_seq_show(seq, v); 27028c2ecf20Sopenharmony_ci} 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci ipv6_route_native_seq_stop(seq, v); 27078c2ecf20Sopenharmony_ci} 27088c2ecf20Sopenharmony_ci#endif 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ciconst struct seq_operations ipv6_route_seq_ops = { 27118c2ecf20Sopenharmony_ci .start = ipv6_route_seq_start, 27128c2ecf20Sopenharmony_ci .next = ipv6_route_seq_next, 27138c2ecf20Sopenharmony_ci .stop = ipv6_route_seq_stop, 27148c2ecf20Sopenharmony_ci .show = ipv6_route_seq_show 27158c2ecf20Sopenharmony_ci}; 27168c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 2717