18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Robert Olsson <robert.olsson@its.uu.se> Uppsala Universitet 58c2ecf20Sopenharmony_ci * & Swedish University of Agricultural Sciences. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Jens Laas <jens.laas@data.slu.se> Swedish University of 88c2ecf20Sopenharmony_ci * Agricultural Sciences. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Hans Liss <hans.liss@its.uu.se> Uppsala Universitet 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This work is based on the LPC-trie which is originally described in: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * An experimental study of compression methods for dynamic tries 158c2ecf20Sopenharmony_ci * Stefan Nilsson and Matti Tikkanen. Algorithmica, 33(1):19-33, 2002. 168c2ecf20Sopenharmony_ci * https://www.csc.kth.se/~snilsson/software/dyntrie2/ 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson 198c2ecf20Sopenharmony_ci * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Code from fib_hash has been reused which includes the following header: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 248c2ecf20Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 258c2ecf20Sopenharmony_ci * interface as the means of communication with the user level. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * IPv4 FIB: lookup engine and maintenance routines. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Substantial contributions to this work comes from: 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * David S. Miller, <davem@davemloft.net> 348c2ecf20Sopenharmony_ci * Stephen Hemminger <shemminger@osdl.org> 358c2ecf20Sopenharmony_ci * Paul E. McKenney <paulmck@us.ibm.com> 368c2ecf20Sopenharmony_ci * Patrick McHardy <kaber@trash.net> 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#include <linux/cache.h> 398c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 408c2ecf20Sopenharmony_ci#include <linux/bitops.h> 418c2ecf20Sopenharmony_ci#include <linux/types.h> 428c2ecf20Sopenharmony_ci#include <linux/kernel.h> 438c2ecf20Sopenharmony_ci#include <linux/mm.h> 448c2ecf20Sopenharmony_ci#include <linux/string.h> 458c2ecf20Sopenharmony_ci#include <linux/socket.h> 468c2ecf20Sopenharmony_ci#include <linux/sockios.h> 478c2ecf20Sopenharmony_ci#include <linux/errno.h> 488c2ecf20Sopenharmony_ci#include <linux/in.h> 498c2ecf20Sopenharmony_ci#include <linux/inet.h> 508c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 518c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 528c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 538c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 548c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 558c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 568c2ecf20Sopenharmony_ci#include <linux/netlink.h> 578c2ecf20Sopenharmony_ci#include <linux/init.h> 588c2ecf20Sopenharmony_ci#include <linux/list.h> 598c2ecf20Sopenharmony_ci#include <linux/slab.h> 608c2ecf20Sopenharmony_ci#include <linux/export.h> 618c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 628c2ecf20Sopenharmony_ci#include <linux/notifier.h> 638c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 648c2ecf20Sopenharmony_ci#include <net/ip.h> 658c2ecf20Sopenharmony_ci#include <net/protocol.h> 668c2ecf20Sopenharmony_ci#include <net/route.h> 678c2ecf20Sopenharmony_ci#include <net/tcp.h> 688c2ecf20Sopenharmony_ci#include <net/sock.h> 698c2ecf20Sopenharmony_ci#include <net/ip_fib.h> 708c2ecf20Sopenharmony_ci#include <net/fib_notifier.h> 718c2ecf20Sopenharmony_ci#include <trace/events/fib.h> 728c2ecf20Sopenharmony_ci#include "fib_lookup.h" 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int call_fib_entry_notifier(struct notifier_block *nb, 758c2ecf20Sopenharmony_ci enum fib_event_type event_type, u32 dst, 768c2ecf20Sopenharmony_ci int dst_len, struct fib_alias *fa, 778c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct fib_entry_notifier_info info = { 808c2ecf20Sopenharmony_ci .info.extack = extack, 818c2ecf20Sopenharmony_ci .dst = dst, 828c2ecf20Sopenharmony_ci .dst_len = dst_len, 838c2ecf20Sopenharmony_ci .fi = fa->fa_info, 848c2ecf20Sopenharmony_ci .tos = fa->fa_tos, 858c2ecf20Sopenharmony_ci .type = fa->fa_type, 868c2ecf20Sopenharmony_ci .tb_id = fa->tb_id, 878c2ecf20Sopenharmony_ci }; 888c2ecf20Sopenharmony_ci return call_fib4_notifier(nb, event_type, &info.info); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int call_fib_entry_notifiers(struct net *net, 928c2ecf20Sopenharmony_ci enum fib_event_type event_type, u32 dst, 938c2ecf20Sopenharmony_ci int dst_len, struct fib_alias *fa, 948c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct fib_entry_notifier_info info = { 978c2ecf20Sopenharmony_ci .info.extack = extack, 988c2ecf20Sopenharmony_ci .dst = dst, 998c2ecf20Sopenharmony_ci .dst_len = dst_len, 1008c2ecf20Sopenharmony_ci .fi = fa->fa_info, 1018c2ecf20Sopenharmony_ci .tos = fa->fa_tos, 1028c2ecf20Sopenharmony_ci .type = fa->fa_type, 1038c2ecf20Sopenharmony_ci .tb_id = fa->tb_id, 1048c2ecf20Sopenharmony_ci }; 1058c2ecf20Sopenharmony_ci return call_fib4_notifiers(net, event_type, &info.info); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define MAX_STAT_DEPTH 32 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define KEYLENGTH (8*sizeof(t_key)) 1118c2ecf20Sopenharmony_ci#define KEY_MAX ((t_key)~0) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_citypedef unsigned int t_key; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define IS_TRIE(n) ((n)->pos >= KEYLENGTH) 1168c2ecf20Sopenharmony_ci#define IS_TNODE(n) ((n)->bits) 1178c2ecf20Sopenharmony_ci#define IS_LEAF(n) (!(n)->bits) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct key_vector { 1208c2ecf20Sopenharmony_ci t_key key; 1218c2ecf20Sopenharmony_ci unsigned char pos; /* 2log(KEYLENGTH) bits needed */ 1228c2ecf20Sopenharmony_ci unsigned char bits; /* 2log(KEYLENGTH) bits needed */ 1238c2ecf20Sopenharmony_ci unsigned char slen; 1248c2ecf20Sopenharmony_ci union { 1258c2ecf20Sopenharmony_ci /* This list pointer if valid if (pos | bits) == 0 (LEAF) */ 1268c2ecf20Sopenharmony_ci struct hlist_head leaf; 1278c2ecf20Sopenharmony_ci /* This array is valid if (pos | bits) > 0 (TNODE) */ 1288c2ecf20Sopenharmony_ci struct key_vector __rcu *tnode[0]; 1298c2ecf20Sopenharmony_ci }; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct tnode { 1338c2ecf20Sopenharmony_ci struct rcu_head rcu; 1348c2ecf20Sopenharmony_ci t_key empty_children; /* KEYLENGTH bits needed */ 1358c2ecf20Sopenharmony_ci t_key full_children; /* KEYLENGTH bits needed */ 1368c2ecf20Sopenharmony_ci struct key_vector __rcu *parent; 1378c2ecf20Sopenharmony_ci struct key_vector kv[1]; 1388c2ecf20Sopenharmony_ci#define tn_bits kv[0].bits 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define TNODE_SIZE(n) offsetof(struct tnode, kv[0].tnode[n]) 1428c2ecf20Sopenharmony_ci#define LEAF_SIZE TNODE_SIZE(1) 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 1458c2ecf20Sopenharmony_cistruct trie_use_stats { 1468c2ecf20Sopenharmony_ci unsigned int gets; 1478c2ecf20Sopenharmony_ci unsigned int backtrack; 1488c2ecf20Sopenharmony_ci unsigned int semantic_match_passed; 1498c2ecf20Sopenharmony_ci unsigned int semantic_match_miss; 1508c2ecf20Sopenharmony_ci unsigned int null_node_hit; 1518c2ecf20Sopenharmony_ci unsigned int resize_node_skipped; 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci#endif 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistruct trie_stat { 1568c2ecf20Sopenharmony_ci unsigned int totdepth; 1578c2ecf20Sopenharmony_ci unsigned int maxdepth; 1588c2ecf20Sopenharmony_ci unsigned int tnodes; 1598c2ecf20Sopenharmony_ci unsigned int leaves; 1608c2ecf20Sopenharmony_ci unsigned int nullpointers; 1618c2ecf20Sopenharmony_ci unsigned int prefixes; 1628c2ecf20Sopenharmony_ci unsigned int nodesizes[MAX_STAT_DEPTH]; 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct trie { 1668c2ecf20Sopenharmony_ci struct key_vector kv[1]; 1678c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 1688c2ecf20Sopenharmony_ci struct trie_use_stats __percpu *stats; 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn); 1738c2ecf20Sopenharmony_cistatic unsigned int tnode_free_size; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * synchronize_rcu after call_rcu for outstanding dirty memory; it should be 1778c2ecf20Sopenharmony_ci * especially useful before resizing the root node with PREEMPT_NONE configs; 1788c2ecf20Sopenharmony_ci * the value was obtained experimentally, aiming to avoid visible slowdown. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem = 512 * 1024; 1818c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem_min = 64 * 1024; 1828c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem_max = 64 * 1024 * 1024; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct kmem_cache *fn_alias_kmem __ro_after_init; 1858c2ecf20Sopenharmony_cistatic struct kmem_cache *trie_leaf_kmem __ro_after_init; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic inline struct tnode *tn_info(struct key_vector *kv) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci return container_of(kv, struct tnode, kv[0]); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* caller must hold RTNL */ 1938c2ecf20Sopenharmony_ci#define node_parent(tn) rtnl_dereference(tn_info(tn)->parent) 1948c2ecf20Sopenharmony_ci#define get_child(tn, i) rtnl_dereference((tn)->tnode[i]) 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* caller must hold RCU read lock or RTNL */ 1978c2ecf20Sopenharmony_ci#define node_parent_rcu(tn) rcu_dereference_rtnl(tn_info(tn)->parent) 1988c2ecf20Sopenharmony_ci#define get_child_rcu(tn, i) rcu_dereference_rtnl((tn)->tnode[i]) 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* wrapper for rcu_assign_pointer */ 2018c2ecf20Sopenharmony_cistatic inline void node_set_parent(struct key_vector *n, struct key_vector *tp) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if (n) 2048c2ecf20Sopenharmony_ci rcu_assign_pointer(tn_info(n)->parent, tp); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER(tn_info(n)->parent, p) 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* This provides us with the number of children in this node, in the case of a 2108c2ecf20Sopenharmony_ci * leaf this will return 0 meaning none of the children are accessible. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic inline unsigned long child_length(const struct key_vector *tn) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return (1ul << tn->bits) & ~(1ul); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#define get_cindex(key, kv) (((key) ^ (kv)->key) >> (kv)->pos) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline unsigned long get_index(t_key key, struct key_vector *kv) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci unsigned long index = key ^ kv->key; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if ((BITS_PER_LONG <= KEYLENGTH) && (KEYLENGTH == kv->pos)) 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return index >> kv->pos; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* To understand this stuff, an understanding of keys and all their bits is 2308c2ecf20Sopenharmony_ci * necessary. Every node in the trie has a key associated with it, but not 2318c2ecf20Sopenharmony_ci * all of the bits in that key are significant. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Consider a node 'n' and its parent 'tp'. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * If n is a leaf, every bit in its key is significant. Its presence is 2368c2ecf20Sopenharmony_ci * necessitated by path compression, since during a tree traversal (when 2378c2ecf20Sopenharmony_ci * searching for a leaf - unless we are doing an insertion) we will completely 2388c2ecf20Sopenharmony_ci * ignore all skipped bits we encounter. Thus we need to verify, at the end of 2398c2ecf20Sopenharmony_ci * a potentially successful search, that we have indeed been walking the 2408c2ecf20Sopenharmony_ci * correct key path. 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Note that we can never "miss" the correct key in the tree if present by 2438c2ecf20Sopenharmony_ci * following the wrong path. Path compression ensures that segments of the key 2448c2ecf20Sopenharmony_ci * that are the same for all keys with a given prefix are skipped, but the 2458c2ecf20Sopenharmony_ci * skipped part *is* identical for each node in the subtrie below the skipped 2468c2ecf20Sopenharmony_ci * bit! trie_insert() in this implementation takes care of that. 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * if n is an internal node - a 'tnode' here, the various parts of its key 2498c2ecf20Sopenharmony_ci * have many different meanings. 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * Example: 2528c2ecf20Sopenharmony_ci * _________________________________________________________________ 2538c2ecf20Sopenharmony_ci * | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C | 2548c2ecf20Sopenharmony_ci * ----------------------------------------------------------------- 2558c2ecf20Sopenharmony_ci * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * _________________________________________________________________ 2588c2ecf20Sopenharmony_ci * | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u | 2598c2ecf20Sopenharmony_ci * ----------------------------------------------------------------- 2608c2ecf20Sopenharmony_ci * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * tp->pos = 22 2638c2ecf20Sopenharmony_ci * tp->bits = 3 2648c2ecf20Sopenharmony_ci * n->pos = 13 2658c2ecf20Sopenharmony_ci * n->bits = 4 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * First, let's just ignore the bits that come before the parent tp, that is 2688c2ecf20Sopenharmony_ci * the bits from (tp->pos + tp->bits) to 31. They are *known* but at this 2698c2ecf20Sopenharmony_ci * point we do not use them for anything. 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the 2728c2ecf20Sopenharmony_ci * index into the parent's child array. That is, they will be used to find 2738c2ecf20Sopenharmony_ci * 'n' among tp's children. 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * The bits from (n->pos + n->bits) to (tp->pos - 1) - "S" - are skipped bits 2768c2ecf20Sopenharmony_ci * for the node n. 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * All the bits we have seen so far are significant to the node n. The rest 2798c2ecf20Sopenharmony_ci * of the bits are really not needed or indeed known in n->key. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into 2828c2ecf20Sopenharmony_ci * n's child array, and will of course be different for each child. 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * The rest of the bits, from 0 to (n->pos -1) - "u" - are completely unknown 2858c2ecf20Sopenharmony_ci * at this point. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const int halve_threshold = 25; 2898c2ecf20Sopenharmony_cistatic const int inflate_threshold = 50; 2908c2ecf20Sopenharmony_cistatic const int halve_threshold_root = 15; 2918c2ecf20Sopenharmony_cistatic const int inflate_threshold_root = 30; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void __alias_free_mem(struct rcu_head *head) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct fib_alias *fa = container_of(head, struct fib_alias, rcu); 2968c2ecf20Sopenharmony_ci kmem_cache_free(fn_alias_kmem, fa); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic inline void alias_free_mem_rcu(struct fib_alias *fa) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci call_rcu(&fa->rcu, __alias_free_mem); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci#define TNODE_VMALLOC_MAX \ 3058c2ecf20Sopenharmony_ci ilog2((SIZE_MAX - TNODE_SIZE(0)) / sizeof(struct key_vector *)) 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void __node_free_rcu(struct rcu_head *head) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct tnode *n = container_of(head, struct tnode, rcu); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!n->tn_bits) 3128c2ecf20Sopenharmony_ci kmem_cache_free(trie_leaf_kmem, n); 3138c2ecf20Sopenharmony_ci else 3148c2ecf20Sopenharmony_ci kvfree(n); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci#define node_free(n) call_rcu(&tn_info(n)->rcu, __node_free_rcu) 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic struct tnode *tnode_alloc(int bits) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci size_t size; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* verify bits is within bounds */ 3248c2ecf20Sopenharmony_ci if (bits > TNODE_VMALLOC_MAX) 3258c2ecf20Sopenharmony_ci return NULL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* determine size and verify it is non-zero and didn't overflow */ 3288c2ecf20Sopenharmony_ci size = TNODE_SIZE(1ul << bits); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (size <= PAGE_SIZE) 3318c2ecf20Sopenharmony_ci return kzalloc(size, GFP_KERNEL); 3328c2ecf20Sopenharmony_ci else 3338c2ecf20Sopenharmony_ci return vzalloc(size); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic inline void empty_child_inc(struct key_vector *n) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci tn_info(n)->empty_children++; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!tn_info(n)->empty_children) 3418c2ecf20Sopenharmony_ci tn_info(n)->full_children++; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic inline void empty_child_dec(struct key_vector *n) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci if (!tn_info(n)->empty_children) 3478c2ecf20Sopenharmony_ci tn_info(n)->full_children--; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci tn_info(n)->empty_children--; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic struct key_vector *leaf_new(t_key key, struct fib_alias *fa) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct key_vector *l; 3558c2ecf20Sopenharmony_ci struct tnode *kv; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci kv = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); 3588c2ecf20Sopenharmony_ci if (!kv) 3598c2ecf20Sopenharmony_ci return NULL; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* initialize key vector */ 3628c2ecf20Sopenharmony_ci l = kv->kv; 3638c2ecf20Sopenharmony_ci l->key = key; 3648c2ecf20Sopenharmony_ci l->pos = 0; 3658c2ecf20Sopenharmony_ci l->bits = 0; 3668c2ecf20Sopenharmony_ci l->slen = fa->fa_slen; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* link leaf to fib alias */ 3698c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&l->leaf); 3708c2ecf20Sopenharmony_ci hlist_add_head(&fa->fa_list, &l->leaf); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return l; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct key_vector *tnode_new(t_key key, int pos, int bits) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci unsigned int shift = pos + bits; 3788c2ecf20Sopenharmony_ci struct key_vector *tn; 3798c2ecf20Sopenharmony_ci struct tnode *tnode; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* verify bits and pos their msb bits clear and values are valid */ 3828c2ecf20Sopenharmony_ci BUG_ON(!bits || (shift > KEYLENGTH)); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci tnode = tnode_alloc(bits); 3858c2ecf20Sopenharmony_ci if (!tnode) 3868c2ecf20Sopenharmony_ci return NULL; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci pr_debug("AT %p s=%zu %zu\n", tnode, TNODE_SIZE(0), 3898c2ecf20Sopenharmony_ci sizeof(struct key_vector *) << bits); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (bits == KEYLENGTH) 3928c2ecf20Sopenharmony_ci tnode->full_children = 1; 3938c2ecf20Sopenharmony_ci else 3948c2ecf20Sopenharmony_ci tnode->empty_children = 1ul << bits; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci tn = tnode->kv; 3978c2ecf20Sopenharmony_ci tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0; 3988c2ecf20Sopenharmony_ci tn->pos = pos; 3998c2ecf20Sopenharmony_ci tn->bits = bits; 4008c2ecf20Sopenharmony_ci tn->slen = pos; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return tn; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* Check whether a tnode 'n' is "full", i.e. it is an internal node 4068c2ecf20Sopenharmony_ci * and no bits are skipped. See discussion in dyntree paper p. 6 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_cistatic inline int tnode_full(struct key_vector *tn, struct key_vector *n) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* Add a child at position i overwriting the old value. 4148c2ecf20Sopenharmony_ci * Update the value of full_children and empty_children. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic void put_child(struct key_vector *tn, unsigned long i, 4178c2ecf20Sopenharmony_ci struct key_vector *n) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct key_vector *chi = get_child(tn, i); 4208c2ecf20Sopenharmony_ci int isfull, wasfull; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci BUG_ON(i >= child_length(tn)); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* update emptyChildren, overflow into fullChildren */ 4258c2ecf20Sopenharmony_ci if (!n && chi) 4268c2ecf20Sopenharmony_ci empty_child_inc(tn); 4278c2ecf20Sopenharmony_ci if (n && !chi) 4288c2ecf20Sopenharmony_ci empty_child_dec(tn); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* update fullChildren */ 4318c2ecf20Sopenharmony_ci wasfull = tnode_full(tn, chi); 4328c2ecf20Sopenharmony_ci isfull = tnode_full(tn, n); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (wasfull && !isfull) 4358c2ecf20Sopenharmony_ci tn_info(tn)->full_children--; 4368c2ecf20Sopenharmony_ci else if (!wasfull && isfull) 4378c2ecf20Sopenharmony_ci tn_info(tn)->full_children++; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (n && (tn->slen < n->slen)) 4408c2ecf20Sopenharmony_ci tn->slen = n->slen; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rcu_assign_pointer(tn->tnode[i], n); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void update_children(struct key_vector *tn) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci unsigned long i; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* update all of the child parent pointers */ 4508c2ecf20Sopenharmony_ci for (i = child_length(tn); i;) { 4518c2ecf20Sopenharmony_ci struct key_vector *inode = get_child(tn, --i); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!inode) 4548c2ecf20Sopenharmony_ci continue; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Either update the children of a tnode that 4578c2ecf20Sopenharmony_ci * already belongs to us or update the child 4588c2ecf20Sopenharmony_ci * to point to ourselves. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci if (node_parent(inode) == tn) 4618c2ecf20Sopenharmony_ci update_children(inode); 4628c2ecf20Sopenharmony_ci else 4638c2ecf20Sopenharmony_ci node_set_parent(inode, tn); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic inline void put_child_root(struct key_vector *tp, t_key key, 4688c2ecf20Sopenharmony_ci struct key_vector *n) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci if (IS_TRIE(tp)) 4718c2ecf20Sopenharmony_ci rcu_assign_pointer(tp->tnode[0], n); 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci put_child(tp, get_index(key, tp), n); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic inline void tnode_free_init(struct key_vector *tn) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci tn_info(tn)->rcu.next = NULL; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic inline void tnode_free_append(struct key_vector *tn, 4828c2ecf20Sopenharmony_ci struct key_vector *n) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci tn_info(n)->rcu.next = tn_info(tn)->rcu.next; 4858c2ecf20Sopenharmony_ci tn_info(tn)->rcu.next = &tn_info(n)->rcu; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic void tnode_free(struct key_vector *tn) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct callback_head *head = &tn_info(tn)->rcu; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci while (head) { 4938c2ecf20Sopenharmony_ci head = head->next; 4948c2ecf20Sopenharmony_ci tnode_free_size += TNODE_SIZE(1ul << tn->bits); 4958c2ecf20Sopenharmony_ci node_free(tn); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci tn = container_of(head, struct tnode, rcu)->kv; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (tnode_free_size >= READ_ONCE(sysctl_fib_sync_mem)) { 5018c2ecf20Sopenharmony_ci tnode_free_size = 0; 5028c2ecf20Sopenharmony_ci synchronize_rcu(); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic struct key_vector *replace(struct trie *t, 5078c2ecf20Sopenharmony_ci struct key_vector *oldtnode, 5088c2ecf20Sopenharmony_ci struct key_vector *tn) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct key_vector *tp = node_parent(oldtnode); 5118c2ecf20Sopenharmony_ci unsigned long i; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* setup the parent pointer out of and back into this node */ 5148c2ecf20Sopenharmony_ci NODE_INIT_PARENT(tn, tp); 5158c2ecf20Sopenharmony_ci put_child_root(tp, tn->key, tn); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* update all of the child parent pointers */ 5188c2ecf20Sopenharmony_ci update_children(tn); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* all pointers should be clean so we are done */ 5218c2ecf20Sopenharmony_ci tnode_free(oldtnode); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* resize children now that oldtnode is freed */ 5248c2ecf20Sopenharmony_ci for (i = child_length(tn); i;) { 5258c2ecf20Sopenharmony_ci struct key_vector *inode = get_child(tn, --i); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* resize child node */ 5288c2ecf20Sopenharmony_ci if (tnode_full(tn, inode)) 5298c2ecf20Sopenharmony_ci tn = resize(t, inode); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return tp; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic struct key_vector *inflate(struct trie *t, 5368c2ecf20Sopenharmony_ci struct key_vector *oldtnode) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct key_vector *tn; 5398c2ecf20Sopenharmony_ci unsigned long i; 5408c2ecf20Sopenharmony_ci t_key m; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci pr_debug("In inflate\n"); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1); 5458c2ecf20Sopenharmony_ci if (!tn) 5468c2ecf20Sopenharmony_ci goto notnode; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* prepare oldtnode to be freed */ 5498c2ecf20Sopenharmony_ci tnode_free_init(oldtnode); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Assemble all of the pointers in our cluster, in this case that 5528c2ecf20Sopenharmony_ci * represents all of the pointers out of our allocated nodes that 5538c2ecf20Sopenharmony_ci * point to existing tnodes and the links between our allocated 5548c2ecf20Sopenharmony_ci * nodes. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci for (i = child_length(oldtnode), m = 1u << tn->pos; i;) { 5578c2ecf20Sopenharmony_ci struct key_vector *inode = get_child(oldtnode, --i); 5588c2ecf20Sopenharmony_ci struct key_vector *node0, *node1; 5598c2ecf20Sopenharmony_ci unsigned long j, k; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* An empty child */ 5628c2ecf20Sopenharmony_ci if (!inode) 5638c2ecf20Sopenharmony_ci continue; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* A leaf or an internal node with skipped bits */ 5668c2ecf20Sopenharmony_ci if (!tnode_full(oldtnode, inode)) { 5678c2ecf20Sopenharmony_ci put_child(tn, get_index(inode->key, tn), inode); 5688c2ecf20Sopenharmony_ci continue; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* drop the node in the old tnode free list */ 5728c2ecf20Sopenharmony_ci tnode_free_append(oldtnode, inode); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* An internal node with two children */ 5758c2ecf20Sopenharmony_ci if (inode->bits == 1) { 5768c2ecf20Sopenharmony_ci put_child(tn, 2 * i + 1, get_child(inode, 1)); 5778c2ecf20Sopenharmony_ci put_child(tn, 2 * i, get_child(inode, 0)); 5788c2ecf20Sopenharmony_ci continue; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* We will replace this node 'inode' with two new 5828c2ecf20Sopenharmony_ci * ones, 'node0' and 'node1', each with half of the 5838c2ecf20Sopenharmony_ci * original children. The two new nodes will have 5848c2ecf20Sopenharmony_ci * a position one bit further down the key and this 5858c2ecf20Sopenharmony_ci * means that the "significant" part of their keys 5868c2ecf20Sopenharmony_ci * (see the discussion near the top of this file) 5878c2ecf20Sopenharmony_ci * will differ by one bit, which will be "0" in 5888c2ecf20Sopenharmony_ci * node0's key and "1" in node1's key. Since we are 5898c2ecf20Sopenharmony_ci * moving the key position by one step, the bit that 5908c2ecf20Sopenharmony_ci * we are moving away from - the bit at position 5918c2ecf20Sopenharmony_ci * (tn->pos) - is the one that will differ between 5928c2ecf20Sopenharmony_ci * node0 and node1. So... we synthesize that bit in the 5938c2ecf20Sopenharmony_ci * two new keys. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1); 5968c2ecf20Sopenharmony_ci if (!node1) 5978c2ecf20Sopenharmony_ci goto nomem; 5988c2ecf20Sopenharmony_ci node0 = tnode_new(inode->key, inode->pos, inode->bits - 1); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci tnode_free_append(tn, node1); 6018c2ecf20Sopenharmony_ci if (!node0) 6028c2ecf20Sopenharmony_ci goto nomem; 6038c2ecf20Sopenharmony_ci tnode_free_append(tn, node0); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* populate child pointers in new nodes */ 6068c2ecf20Sopenharmony_ci for (k = child_length(inode), j = k / 2; j;) { 6078c2ecf20Sopenharmony_ci put_child(node1, --j, get_child(inode, --k)); 6088c2ecf20Sopenharmony_ci put_child(node0, j, get_child(inode, j)); 6098c2ecf20Sopenharmony_ci put_child(node1, --j, get_child(inode, --k)); 6108c2ecf20Sopenharmony_ci put_child(node0, j, get_child(inode, j)); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* link new nodes to parent */ 6148c2ecf20Sopenharmony_ci NODE_INIT_PARENT(node1, tn); 6158c2ecf20Sopenharmony_ci NODE_INIT_PARENT(node0, tn); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* link parent to nodes */ 6188c2ecf20Sopenharmony_ci put_child(tn, 2 * i + 1, node1); 6198c2ecf20Sopenharmony_ci put_child(tn, 2 * i, node0); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* setup the parent pointers into and out of this node */ 6238c2ecf20Sopenharmony_ci return replace(t, oldtnode, tn); 6248c2ecf20Sopenharmony_cinomem: 6258c2ecf20Sopenharmony_ci /* all pointers should be clean so we are done */ 6268c2ecf20Sopenharmony_ci tnode_free(tn); 6278c2ecf20Sopenharmony_cinotnode: 6288c2ecf20Sopenharmony_ci return NULL; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic struct key_vector *halve(struct trie *t, 6328c2ecf20Sopenharmony_ci struct key_vector *oldtnode) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct key_vector *tn; 6358c2ecf20Sopenharmony_ci unsigned long i; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci pr_debug("In halve\n"); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1); 6408c2ecf20Sopenharmony_ci if (!tn) 6418c2ecf20Sopenharmony_ci goto notnode; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* prepare oldtnode to be freed */ 6448c2ecf20Sopenharmony_ci tnode_free_init(oldtnode); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Assemble all of the pointers in our cluster, in this case that 6478c2ecf20Sopenharmony_ci * represents all of the pointers out of our allocated nodes that 6488c2ecf20Sopenharmony_ci * point to existing tnodes and the links between our allocated 6498c2ecf20Sopenharmony_ci * nodes. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci for (i = child_length(oldtnode); i;) { 6528c2ecf20Sopenharmony_ci struct key_vector *node1 = get_child(oldtnode, --i); 6538c2ecf20Sopenharmony_ci struct key_vector *node0 = get_child(oldtnode, --i); 6548c2ecf20Sopenharmony_ci struct key_vector *inode; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* At least one of the children is empty */ 6578c2ecf20Sopenharmony_ci if (!node1 || !node0) { 6588c2ecf20Sopenharmony_ci put_child(tn, i / 2, node1 ? : node0); 6598c2ecf20Sopenharmony_ci continue; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* Two nonempty children */ 6638c2ecf20Sopenharmony_ci inode = tnode_new(node0->key, oldtnode->pos, 1); 6648c2ecf20Sopenharmony_ci if (!inode) 6658c2ecf20Sopenharmony_ci goto nomem; 6668c2ecf20Sopenharmony_ci tnode_free_append(tn, inode); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* initialize pointers out of node */ 6698c2ecf20Sopenharmony_ci put_child(inode, 1, node1); 6708c2ecf20Sopenharmony_ci put_child(inode, 0, node0); 6718c2ecf20Sopenharmony_ci NODE_INIT_PARENT(inode, tn); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* link parent to node */ 6748c2ecf20Sopenharmony_ci put_child(tn, i / 2, inode); 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* setup the parent pointers into and out of this node */ 6788c2ecf20Sopenharmony_ci return replace(t, oldtnode, tn); 6798c2ecf20Sopenharmony_cinomem: 6808c2ecf20Sopenharmony_ci /* all pointers should be clean so we are done */ 6818c2ecf20Sopenharmony_ci tnode_free(tn); 6828c2ecf20Sopenharmony_cinotnode: 6838c2ecf20Sopenharmony_ci return NULL; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic struct key_vector *collapse(struct trie *t, 6878c2ecf20Sopenharmony_ci struct key_vector *oldtnode) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct key_vector *n, *tp; 6908c2ecf20Sopenharmony_ci unsigned long i; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* scan the tnode looking for that one child that might still exist */ 6938c2ecf20Sopenharmony_ci for (n = NULL, i = child_length(oldtnode); !n && i;) 6948c2ecf20Sopenharmony_ci n = get_child(oldtnode, --i); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* compress one level */ 6978c2ecf20Sopenharmony_ci tp = node_parent(oldtnode); 6988c2ecf20Sopenharmony_ci put_child_root(tp, oldtnode->key, n); 6998c2ecf20Sopenharmony_ci node_set_parent(n, tp); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* drop dead node */ 7028c2ecf20Sopenharmony_ci node_free(oldtnode); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return tp; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic unsigned char update_suffix(struct key_vector *tn) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci unsigned char slen = tn->pos; 7108c2ecf20Sopenharmony_ci unsigned long stride, i; 7118c2ecf20Sopenharmony_ci unsigned char slen_max; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* only vector 0 can have a suffix length greater than or equal to 7148c2ecf20Sopenharmony_ci * tn->pos + tn->bits, the second highest node will have a suffix 7158c2ecf20Sopenharmony_ci * length at most of tn->pos + tn->bits - 1 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci slen_max = min_t(unsigned char, tn->pos + tn->bits - 1, tn->slen); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* search though the list of children looking for nodes that might 7208c2ecf20Sopenharmony_ci * have a suffix greater than the one we currently have. This is 7218c2ecf20Sopenharmony_ci * why we start with a stride of 2 since a stride of 1 would 7228c2ecf20Sopenharmony_ci * represent the nodes with suffix length equal to tn->pos 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci for (i = 0, stride = 0x2ul ; i < child_length(tn); i += stride) { 7258c2ecf20Sopenharmony_ci struct key_vector *n = get_child(tn, i); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!n || (n->slen <= slen)) 7288c2ecf20Sopenharmony_ci continue; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* update stride and slen based on new value */ 7318c2ecf20Sopenharmony_ci stride <<= (n->slen - slen); 7328c2ecf20Sopenharmony_ci slen = n->slen; 7338c2ecf20Sopenharmony_ci i &= ~(stride - 1); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* stop searching if we have hit the maximum possible value */ 7368c2ecf20Sopenharmony_ci if (slen >= slen_max) 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci tn->slen = slen; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return slen; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of 7468c2ecf20Sopenharmony_ci * the Helsinki University of Technology and Matti Tikkanen of Nokia 7478c2ecf20Sopenharmony_ci * Telecommunications, page 6: 7488c2ecf20Sopenharmony_ci * "A node is doubled if the ratio of non-empty children to all 7498c2ecf20Sopenharmony_ci * children in the *doubled* node is at least 'high'." 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * 'high' in this instance is the variable 'inflate_threshold'. It 7528c2ecf20Sopenharmony_ci * is expressed as a percentage, so we multiply it with 7538c2ecf20Sopenharmony_ci * child_length() and instead of multiplying by 2 (since the 7548c2ecf20Sopenharmony_ci * child array will be doubled by inflate()) and multiplying 7558c2ecf20Sopenharmony_ci * the left-hand side by 100 (to handle the percentage thing) we 7568c2ecf20Sopenharmony_ci * multiply the left-hand side by 50. 7578c2ecf20Sopenharmony_ci * 7588c2ecf20Sopenharmony_ci * The left-hand side may look a bit weird: child_length(tn) 7598c2ecf20Sopenharmony_ci * - tn->empty_children is of course the number of non-null children 7608c2ecf20Sopenharmony_ci * in the current node. tn->full_children is the number of "full" 7618c2ecf20Sopenharmony_ci * children, that is non-null tnodes with a skip value of 0. 7628c2ecf20Sopenharmony_ci * All of those will be doubled in the resulting inflated tnode, so 7638c2ecf20Sopenharmony_ci * we just count them one extra time here. 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * A clearer way to write this would be: 7668c2ecf20Sopenharmony_ci * 7678c2ecf20Sopenharmony_ci * to_be_doubled = tn->full_children; 7688c2ecf20Sopenharmony_ci * not_to_be_doubled = child_length(tn) - tn->empty_children - 7698c2ecf20Sopenharmony_ci * tn->full_children; 7708c2ecf20Sopenharmony_ci * 7718c2ecf20Sopenharmony_ci * new_child_length = child_length(tn) * 2; 7728c2ecf20Sopenharmony_ci * 7738c2ecf20Sopenharmony_ci * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / 7748c2ecf20Sopenharmony_ci * new_child_length; 7758c2ecf20Sopenharmony_ci * if (new_fill_factor >= inflate_threshold) 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * ...and so on, tho it would mess up the while () loop. 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * anyway, 7808c2ecf20Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= 7818c2ecf20Sopenharmony_ci * inflate_threshold 7828c2ecf20Sopenharmony_ci * 7838c2ecf20Sopenharmony_ci * avoid a division: 7848c2ecf20Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) >= 7858c2ecf20Sopenharmony_ci * inflate_threshold * new_child_length 7868c2ecf20Sopenharmony_ci * 7878c2ecf20Sopenharmony_ci * expand not_to_be_doubled and to_be_doubled, and shorten: 7888c2ecf20Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children + 7898c2ecf20Sopenharmony_ci * tn->full_children) >= inflate_threshold * new_child_length 7908c2ecf20Sopenharmony_ci * 7918c2ecf20Sopenharmony_ci * expand new_child_length: 7928c2ecf20Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children + 7938c2ecf20Sopenharmony_ci * tn->full_children) >= 7948c2ecf20Sopenharmony_ci * inflate_threshold * child_length(tn) * 2 7958c2ecf20Sopenharmony_ci * 7968c2ecf20Sopenharmony_ci * shorten again: 7978c2ecf20Sopenharmony_ci * 50 * (tn->full_children + child_length(tn) - 7988c2ecf20Sopenharmony_ci * tn->empty_children) >= inflate_threshold * 7998c2ecf20Sopenharmony_ci * child_length(tn) 8008c2ecf20Sopenharmony_ci * 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_cistatic inline bool should_inflate(struct key_vector *tp, struct key_vector *tn) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci unsigned long used = child_length(tn); 8058c2ecf20Sopenharmony_ci unsigned long threshold = used; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* Keep root node larger */ 8088c2ecf20Sopenharmony_ci threshold *= IS_TRIE(tp) ? inflate_threshold_root : inflate_threshold; 8098c2ecf20Sopenharmony_ci used -= tn_info(tn)->empty_children; 8108c2ecf20Sopenharmony_ci used += tn_info(tn)->full_children; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* if bits == KEYLENGTH then pos = 0, and will fail below */ 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return (used > 1) && tn->pos && ((50 * used) >= threshold); 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic inline bool should_halve(struct key_vector *tp, struct key_vector *tn) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci unsigned long used = child_length(tn); 8208c2ecf20Sopenharmony_ci unsigned long threshold = used; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Keep root node larger */ 8238c2ecf20Sopenharmony_ci threshold *= IS_TRIE(tp) ? halve_threshold_root : halve_threshold; 8248c2ecf20Sopenharmony_ci used -= tn_info(tn)->empty_children; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* if bits == KEYLENGTH then used = 100% on wrap, and will fail below */ 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return (used > 1) && (tn->bits > 1) && ((100 * used) < threshold); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic inline bool should_collapse(struct key_vector *tn) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci unsigned long used = child_length(tn); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci used -= tn_info(tn)->empty_children; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* account for bits == KEYLENGTH case */ 8388c2ecf20Sopenharmony_ci if ((tn->bits == KEYLENGTH) && tn_info(tn)->full_children) 8398c2ecf20Sopenharmony_ci used -= KEY_MAX; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* One child or none, time to drop us from the trie */ 8428c2ecf20Sopenharmony_ci return used < 2; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci#define MAX_WORK 10 8468c2ecf20Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 8498c2ecf20Sopenharmony_ci struct trie_use_stats __percpu *stats = t->stats; 8508c2ecf20Sopenharmony_ci#endif 8518c2ecf20Sopenharmony_ci struct key_vector *tp = node_parent(tn); 8528c2ecf20Sopenharmony_ci unsigned long cindex = get_index(tn->key, tp); 8538c2ecf20Sopenharmony_ci int max_work = MAX_WORK; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n", 8568c2ecf20Sopenharmony_ci tn, inflate_threshold, halve_threshold); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* track the tnode via the pointer from the parent instead of 8598c2ecf20Sopenharmony_ci * doing it ourselves. This way we can let RCU fully do its 8608c2ecf20Sopenharmony_ci * thing without us interfering 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_ci BUG_ON(tn != get_child(tp, cindex)); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* Double as long as the resulting node has a number of 8658c2ecf20Sopenharmony_ci * nonempty nodes that are above the threshold. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci while (should_inflate(tp, tn) && max_work) { 8688c2ecf20Sopenharmony_ci tp = inflate(t, tn); 8698c2ecf20Sopenharmony_ci if (!tp) { 8708c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 8718c2ecf20Sopenharmony_ci this_cpu_inc(stats->resize_node_skipped); 8728c2ecf20Sopenharmony_ci#endif 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci max_work--; 8778c2ecf20Sopenharmony_ci tn = get_child(tp, cindex); 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* update parent in case inflate failed */ 8818c2ecf20Sopenharmony_ci tp = node_parent(tn); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Return if at least one inflate is run */ 8848c2ecf20Sopenharmony_ci if (max_work != MAX_WORK) 8858c2ecf20Sopenharmony_ci return tp; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* Halve as long as the number of empty children in this 8888c2ecf20Sopenharmony_ci * node is above threshold. 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_ci while (should_halve(tp, tn) && max_work) { 8918c2ecf20Sopenharmony_ci tp = halve(t, tn); 8928c2ecf20Sopenharmony_ci if (!tp) { 8938c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 8948c2ecf20Sopenharmony_ci this_cpu_inc(stats->resize_node_skipped); 8958c2ecf20Sopenharmony_ci#endif 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci max_work--; 9008c2ecf20Sopenharmony_ci tn = get_child(tp, cindex); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* Only one child remains */ 9048c2ecf20Sopenharmony_ci if (should_collapse(tn)) 9058c2ecf20Sopenharmony_ci return collapse(t, tn); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* update parent in case halve failed */ 9088c2ecf20Sopenharmony_ci return node_parent(tn); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic void node_pull_suffix(struct key_vector *tn, unsigned char slen) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci unsigned char node_slen = tn->slen; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci while ((node_slen > tn->pos) && (node_slen > slen)) { 9168c2ecf20Sopenharmony_ci slen = update_suffix(tn); 9178c2ecf20Sopenharmony_ci if (node_slen == slen) 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci tn = node_parent(tn); 9218c2ecf20Sopenharmony_ci node_slen = tn->slen; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic void node_push_suffix(struct key_vector *tn, unsigned char slen) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci while (tn->slen < slen) { 9288c2ecf20Sopenharmony_ci tn->slen = slen; 9298c2ecf20Sopenharmony_ci tn = node_parent(tn); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */ 9348c2ecf20Sopenharmony_cistatic struct key_vector *fib_find_node(struct trie *t, 9358c2ecf20Sopenharmony_ci struct key_vector **tp, u32 key) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct key_vector *pn, *n = t->kv; 9388c2ecf20Sopenharmony_ci unsigned long index = 0; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci do { 9418c2ecf20Sopenharmony_ci pn = n; 9428c2ecf20Sopenharmony_ci n = get_child_rcu(n, index); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (!n) 9458c2ecf20Sopenharmony_ci break; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci index = get_cindex(key, n); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* This bit of code is a bit tricky but it combines multiple 9508c2ecf20Sopenharmony_ci * checks into a single check. The prefix consists of the 9518c2ecf20Sopenharmony_ci * prefix plus zeros for the bits in the cindex. The index 9528c2ecf20Sopenharmony_ci * is the difference between the key and this value. From 9538c2ecf20Sopenharmony_ci * this we can actually derive several pieces of data. 9548c2ecf20Sopenharmony_ci * if (index >= (1ul << bits)) 9558c2ecf20Sopenharmony_ci * we have a mismatch in skip bits and failed 9568c2ecf20Sopenharmony_ci * else 9578c2ecf20Sopenharmony_ci * we know the value is cindex 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * This check is safe even if bits == KEYLENGTH due to the 9608c2ecf20Sopenharmony_ci * fact that we can only allocate a node with 32 bits if a 9618c2ecf20Sopenharmony_ci * long is greater than 32 bits. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci if (index >= (1ul << n->bits)) { 9648c2ecf20Sopenharmony_ci n = NULL; 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* keep searching until we find a perfect match leaf or NULL */ 9698c2ecf20Sopenharmony_ci } while (IS_TNODE(n)); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci *tp = pn; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return n; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/* Return the first fib alias matching TOS with 9778c2ecf20Sopenharmony_ci * priority less than or equal to PRIO. 9788c2ecf20Sopenharmony_ci * If 'find_first' is set, return the first matching 9798c2ecf20Sopenharmony_ci * fib alias, regardless of TOS and priority. 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_cistatic struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, 9828c2ecf20Sopenharmony_ci u8 tos, u32 prio, u32 tb_id, 9838c2ecf20Sopenharmony_ci bool find_first) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct fib_alias *fa; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (!fah) 9888c2ecf20Sopenharmony_ci return NULL; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci hlist_for_each_entry(fa, fah, fa_list) { 9918c2ecf20Sopenharmony_ci if (fa->fa_slen < slen) 9928c2ecf20Sopenharmony_ci continue; 9938c2ecf20Sopenharmony_ci if (fa->fa_slen != slen) 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci if (fa->tb_id > tb_id) 9968c2ecf20Sopenharmony_ci continue; 9978c2ecf20Sopenharmony_ci if (fa->tb_id != tb_id) 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci if (find_first) 10008c2ecf20Sopenharmony_ci return fa; 10018c2ecf20Sopenharmony_ci if (fa->fa_tos > tos) 10028c2ecf20Sopenharmony_ci continue; 10038c2ecf20Sopenharmony_ci if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) 10048c2ecf20Sopenharmony_ci return fa; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci return NULL; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic struct fib_alias * 10118c2ecf20Sopenharmony_cifib_find_matching_alias(struct net *net, const struct fib_rt_info *fri) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci u8 slen = KEYLENGTH - fri->dst_len; 10148c2ecf20Sopenharmony_ci struct key_vector *l, *tp; 10158c2ecf20Sopenharmony_ci struct fib_table *tb; 10168c2ecf20Sopenharmony_ci struct fib_alias *fa; 10178c2ecf20Sopenharmony_ci struct trie *t; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci tb = fib_get_table(net, fri->tb_id); 10208c2ecf20Sopenharmony_ci if (!tb) 10218c2ecf20Sopenharmony_ci return NULL; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci t = (struct trie *)tb->tb_data; 10248c2ecf20Sopenharmony_ci l = fib_find_node(t, &tp, be32_to_cpu(fri->dst)); 10258c2ecf20Sopenharmony_ci if (!l) 10268c2ecf20Sopenharmony_ci return NULL; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 10298c2ecf20Sopenharmony_ci if (fa->fa_slen == slen && fa->tb_id == fri->tb_id && 10308c2ecf20Sopenharmony_ci fa->fa_tos == fri->tos && fa->fa_info == fri->fi && 10318c2ecf20Sopenharmony_ci fa->fa_type == fri->type) 10328c2ecf20Sopenharmony_ci return fa; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return NULL; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_civoid fib_alias_hw_flags_set(struct net *net, const struct fib_rt_info *fri) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct fib_alias *fa_match; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci rcu_read_lock(); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci fa_match = fib_find_matching_alias(net, fri); 10458c2ecf20Sopenharmony_ci if (!fa_match) 10468c2ecf20Sopenharmony_ci goto out; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci fa_match->offload = fri->offload; 10498c2ecf20Sopenharmony_ci fa_match->trap = fri->trap; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ciout: 10528c2ecf20Sopenharmony_ci rcu_read_unlock(); 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_alias_hw_flags_set); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void trie_rebalance(struct trie *t, struct key_vector *tn) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci while (!IS_TRIE(tn)) 10598c2ecf20Sopenharmony_ci tn = resize(t, tn); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic int fib_insert_node(struct trie *t, struct key_vector *tp, 10638c2ecf20Sopenharmony_ci struct fib_alias *new, t_key key) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct key_vector *n, *l; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci l = leaf_new(key, new); 10688c2ecf20Sopenharmony_ci if (!l) 10698c2ecf20Sopenharmony_ci goto noleaf; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* retrieve child from parent node */ 10728c2ecf20Sopenharmony_ci n = get_child(tp, get_index(key, tp)); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* Case 2: n is a LEAF or a TNODE and the key doesn't match. 10758c2ecf20Sopenharmony_ci * 10768c2ecf20Sopenharmony_ci * Add a new tnode here 10778c2ecf20Sopenharmony_ci * first tnode need some special handling 10788c2ecf20Sopenharmony_ci * leaves us in position for handling as case 3 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci if (n) { 10818c2ecf20Sopenharmony_ci struct key_vector *tn; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci tn = tnode_new(key, __fls(key ^ n->key), 1); 10848c2ecf20Sopenharmony_ci if (!tn) 10858c2ecf20Sopenharmony_ci goto notnode; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* initialize routes out of node */ 10888c2ecf20Sopenharmony_ci NODE_INIT_PARENT(tn, tp); 10898c2ecf20Sopenharmony_ci put_child(tn, get_index(key, tn) ^ 1, n); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* start adding routes into the node */ 10928c2ecf20Sopenharmony_ci put_child_root(tp, key, tn); 10938c2ecf20Sopenharmony_ci node_set_parent(n, tn); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* parent now has a NULL spot where the leaf can go */ 10968c2ecf20Sopenharmony_ci tp = tn; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* Case 3: n is NULL, and will just insert a new leaf */ 11008c2ecf20Sopenharmony_ci node_push_suffix(tp, new->fa_slen); 11018c2ecf20Sopenharmony_ci NODE_INIT_PARENT(l, tp); 11028c2ecf20Sopenharmony_ci put_child_root(tp, key, l); 11038c2ecf20Sopenharmony_ci trie_rebalance(t, tp); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_cinotnode: 11078c2ecf20Sopenharmony_ci node_free(l); 11088c2ecf20Sopenharmony_cinoleaf: 11098c2ecf20Sopenharmony_ci return -ENOMEM; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic int fib_insert_alias(struct trie *t, struct key_vector *tp, 11138c2ecf20Sopenharmony_ci struct key_vector *l, struct fib_alias *new, 11148c2ecf20Sopenharmony_ci struct fib_alias *fa, t_key key) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci if (!l) 11178c2ecf20Sopenharmony_ci return fib_insert_node(t, tp, new, key); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (fa) { 11208c2ecf20Sopenharmony_ci hlist_add_before_rcu(&new->fa_list, &fa->fa_list); 11218c2ecf20Sopenharmony_ci } else { 11228c2ecf20Sopenharmony_ci struct fib_alias *last; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci hlist_for_each_entry(last, &l->leaf, fa_list) { 11258c2ecf20Sopenharmony_ci if (new->fa_slen < last->fa_slen) 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci if ((new->fa_slen == last->fa_slen) && 11288c2ecf20Sopenharmony_ci (new->tb_id > last->tb_id)) 11298c2ecf20Sopenharmony_ci break; 11308c2ecf20Sopenharmony_ci fa = last; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (fa) 11348c2ecf20Sopenharmony_ci hlist_add_behind_rcu(&new->fa_list, &fa->fa_list); 11358c2ecf20Sopenharmony_ci else 11368c2ecf20Sopenharmony_ci hlist_add_head_rcu(&new->fa_list, &l->leaf); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* if we added to the tail node then we need to update slen */ 11408c2ecf20Sopenharmony_ci if (l->slen < new->fa_slen) { 11418c2ecf20Sopenharmony_ci l->slen = new->fa_slen; 11428c2ecf20Sopenharmony_ci node_push_suffix(tp, new->fa_slen); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci if (plen > KEYLENGTH) { 11518c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid prefix length"); 11528c2ecf20Sopenharmony_ci return false; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if ((plen < KEYLENGTH) && (key << plen)) { 11568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 11578c2ecf20Sopenharmony_ci "Invalid prefix for given prefix length"); 11588c2ecf20Sopenharmony_ci return false; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return true; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp, 11658c2ecf20Sopenharmony_ci struct key_vector *l, struct fib_alias *old); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */ 11688c2ecf20Sopenharmony_ciint fib_table_insert(struct net *net, struct fib_table *tb, 11698c2ecf20Sopenharmony_ci struct fib_config *cfg, struct netlink_ext_ack *extack) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 11728c2ecf20Sopenharmony_ci struct fib_alias *fa, *new_fa; 11738c2ecf20Sopenharmony_ci struct key_vector *l, *tp; 11748c2ecf20Sopenharmony_ci u16 nlflags = NLM_F_EXCL; 11758c2ecf20Sopenharmony_ci struct fib_info *fi; 11768c2ecf20Sopenharmony_ci u8 plen = cfg->fc_dst_len; 11778c2ecf20Sopenharmony_ci u8 slen = KEYLENGTH - plen; 11788c2ecf20Sopenharmony_ci u8 tos = cfg->fc_tos; 11798c2ecf20Sopenharmony_ci u32 key; 11808c2ecf20Sopenharmony_ci int err; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci key = ntohl(cfg->fc_dst); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (!fib_valid_key_len(key, plen, extack)) 11858c2ecf20Sopenharmony_ci return -EINVAL; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci fi = fib_create_info(cfg, extack); 11908c2ecf20Sopenharmony_ci if (IS_ERR(fi)) { 11918c2ecf20Sopenharmony_ci err = PTR_ERR(fi); 11928c2ecf20Sopenharmony_ci goto err; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci l = fib_find_node(t, &tp, key); 11968c2ecf20Sopenharmony_ci fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, 11978c2ecf20Sopenharmony_ci tb->tb_id, false) : NULL; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* Now fa, if non-NULL, points to the first fib alias 12008c2ecf20Sopenharmony_ci * with the same keys [prefix,tos,priority], if such key already 12018c2ecf20Sopenharmony_ci * exists or to the node before which we will insert new one. 12028c2ecf20Sopenharmony_ci * 12038c2ecf20Sopenharmony_ci * If fa is NULL, we will need to allocate a new one and 12048c2ecf20Sopenharmony_ci * insert to the tail of the section matching the suffix length 12058c2ecf20Sopenharmony_ci * of the new alias. 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (fa && fa->fa_tos == tos && 12098c2ecf20Sopenharmony_ci fa->fa_info->fib_priority == fi->fib_priority) { 12108c2ecf20Sopenharmony_ci struct fib_alias *fa_first, *fa_match; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci err = -EEXIST; 12138c2ecf20Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_EXCL) 12148c2ecf20Sopenharmony_ci goto out; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci nlflags &= ~NLM_F_EXCL; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* We have 2 goals: 12198c2ecf20Sopenharmony_ci * 1. Find exact match for type, scope, fib_info to avoid 12208c2ecf20Sopenharmony_ci * duplicate routes 12218c2ecf20Sopenharmony_ci * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_ci fa_match = NULL; 12248c2ecf20Sopenharmony_ci fa_first = fa; 12258c2ecf20Sopenharmony_ci hlist_for_each_entry_from(fa, fa_list) { 12268c2ecf20Sopenharmony_ci if ((fa->fa_slen != slen) || 12278c2ecf20Sopenharmony_ci (fa->tb_id != tb->tb_id) || 12288c2ecf20Sopenharmony_ci (fa->fa_tos != tos)) 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci if (fa->fa_info->fib_priority != fi->fib_priority) 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci if (fa->fa_type == cfg->fc_type && 12338c2ecf20Sopenharmony_ci fa->fa_info == fi) { 12348c2ecf20Sopenharmony_ci fa_match = fa; 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_REPLACE) { 12408c2ecf20Sopenharmony_ci struct fib_info *fi_drop; 12418c2ecf20Sopenharmony_ci u8 state; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci nlflags |= NLM_F_REPLACE; 12448c2ecf20Sopenharmony_ci fa = fa_first; 12458c2ecf20Sopenharmony_ci if (fa_match) { 12468c2ecf20Sopenharmony_ci if (fa == fa_match) 12478c2ecf20Sopenharmony_ci err = 0; 12488c2ecf20Sopenharmony_ci goto out; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci err = -ENOBUFS; 12518c2ecf20Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 12528c2ecf20Sopenharmony_ci if (!new_fa) 12538c2ecf20Sopenharmony_ci goto out; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci fi_drop = fa->fa_info; 12568c2ecf20Sopenharmony_ci new_fa->fa_tos = fa->fa_tos; 12578c2ecf20Sopenharmony_ci new_fa->fa_info = fi; 12588c2ecf20Sopenharmony_ci new_fa->fa_type = cfg->fc_type; 12598c2ecf20Sopenharmony_ci state = fa->fa_state; 12608c2ecf20Sopenharmony_ci new_fa->fa_state = state & ~FA_S_ACCESSED; 12618c2ecf20Sopenharmony_ci new_fa->fa_slen = fa->fa_slen; 12628c2ecf20Sopenharmony_ci new_fa->tb_id = tb->tb_id; 12638c2ecf20Sopenharmony_ci new_fa->fa_default = -1; 12648c2ecf20Sopenharmony_ci new_fa->offload = 0; 12658c2ecf20Sopenharmony_ci new_fa->trap = 0; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (fib_find_alias(&l->leaf, fa->fa_slen, 0, 0, 12708c2ecf20Sopenharmony_ci tb->tb_id, true) == new_fa) { 12718c2ecf20Sopenharmony_ci enum fib_event_type fib_event; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 12748c2ecf20Sopenharmony_ci err = call_fib_entry_notifiers(net, fib_event, 12758c2ecf20Sopenharmony_ci key, plen, 12768c2ecf20Sopenharmony_ci new_fa, extack); 12778c2ecf20Sopenharmony_ci if (err) { 12788c2ecf20Sopenharmony_ci hlist_replace_rcu(&new_fa->fa_list, 12798c2ecf20Sopenharmony_ci &fa->fa_list); 12808c2ecf20Sopenharmony_ci goto out_free_new_fa; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, 12858c2ecf20Sopenharmony_ci tb->tb_id, &cfg->fc_nlinfo, nlflags); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci alias_free_mem_rcu(fa); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci fib_release_info(fi_drop); 12908c2ecf20Sopenharmony_ci if (state & FA_S_ACCESSED) 12918c2ecf20Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci goto succeeded; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci /* Error if we find a perfect match which 12968c2ecf20Sopenharmony_ci * uses the same scope, type, and nexthop 12978c2ecf20Sopenharmony_ci * information. 12988c2ecf20Sopenharmony_ci */ 12998c2ecf20Sopenharmony_ci if (fa_match) 13008c2ecf20Sopenharmony_ci goto out; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_APPEND) 13038c2ecf20Sopenharmony_ci nlflags |= NLM_F_APPEND; 13048c2ecf20Sopenharmony_ci else 13058c2ecf20Sopenharmony_ci fa = fa_first; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci err = -ENOENT; 13088c2ecf20Sopenharmony_ci if (!(cfg->fc_nlflags & NLM_F_CREATE)) 13098c2ecf20Sopenharmony_ci goto out; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci nlflags |= NLM_F_CREATE; 13128c2ecf20Sopenharmony_ci err = -ENOBUFS; 13138c2ecf20Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 13148c2ecf20Sopenharmony_ci if (!new_fa) 13158c2ecf20Sopenharmony_ci goto out; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci new_fa->fa_info = fi; 13188c2ecf20Sopenharmony_ci new_fa->fa_tos = tos; 13198c2ecf20Sopenharmony_ci new_fa->fa_type = cfg->fc_type; 13208c2ecf20Sopenharmony_ci new_fa->fa_state = 0; 13218c2ecf20Sopenharmony_ci new_fa->fa_slen = slen; 13228c2ecf20Sopenharmony_ci new_fa->tb_id = tb->tb_id; 13238c2ecf20Sopenharmony_ci new_fa->fa_default = -1; 13248c2ecf20Sopenharmony_ci new_fa->offload = 0; 13258c2ecf20Sopenharmony_ci new_fa->trap = 0; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* Insert new entry to the list. */ 13288c2ecf20Sopenharmony_ci err = fib_insert_alias(t, tp, l, new_fa, fa, key); 13298c2ecf20Sopenharmony_ci if (err) 13308c2ecf20Sopenharmony_ci goto out_free_new_fa; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci /* The alias was already inserted, so the node must exist. */ 13338c2ecf20Sopenharmony_ci l = l ? l : fib_find_node(t, &tp, key); 13348c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!l)) { 13358c2ecf20Sopenharmony_ci err = -ENOENT; 13368c2ecf20Sopenharmony_ci goto out_free_new_fa; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (fib_find_alias(&l->leaf, new_fa->fa_slen, 0, 0, tb->tb_id, true) == 13408c2ecf20Sopenharmony_ci new_fa) { 13418c2ecf20Sopenharmony_ci enum fib_event_type fib_event; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 13448c2ecf20Sopenharmony_ci err = call_fib_entry_notifiers(net, fib_event, key, plen, 13458c2ecf20Sopenharmony_ci new_fa, extack); 13468c2ecf20Sopenharmony_ci if (err) 13478c2ecf20Sopenharmony_ci goto out_remove_new_fa; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (!plen) 13518c2ecf20Sopenharmony_ci tb->tb_num_default++; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 13548c2ecf20Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, 13558c2ecf20Sopenharmony_ci &cfg->fc_nlinfo, nlflags); 13568c2ecf20Sopenharmony_cisucceeded: 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ciout_remove_new_fa: 13608c2ecf20Sopenharmony_ci fib_remove_alias(t, tp, l, new_fa); 13618c2ecf20Sopenharmony_ciout_free_new_fa: 13628c2ecf20Sopenharmony_ci kmem_cache_free(fn_alias_kmem, new_fa); 13638c2ecf20Sopenharmony_ciout: 13648c2ecf20Sopenharmony_ci fib_release_info(fi); 13658c2ecf20Sopenharmony_cierr: 13668c2ecf20Sopenharmony_ci return err; 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic inline t_key prefix_mismatch(t_key key, struct key_vector *n) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci t_key prefix = n->key; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci return (key ^ prefix) & (prefix | -prefix); 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cibool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags, 13778c2ecf20Sopenharmony_ci const struct flowi4 *flp) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci if (nhc->nhc_flags & RTNH_F_DEAD) 13808c2ecf20Sopenharmony_ci return false; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (ip_ignore_linkdown(nhc->nhc_dev) && 13838c2ecf20Sopenharmony_ci nhc->nhc_flags & RTNH_F_LINKDOWN && 13848c2ecf20Sopenharmony_ci !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE)) 13858c2ecf20Sopenharmony_ci return false; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (!(flp->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) { 13888c2ecf20Sopenharmony_ci if (flp->flowi4_oif && 13898c2ecf20Sopenharmony_ci flp->flowi4_oif != nhc->nhc_oif) 13908c2ecf20Sopenharmony_ci return false; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci return true; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci/* should be called with rcu_read_lock */ 13978c2ecf20Sopenharmony_ciint fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, 13988c2ecf20Sopenharmony_ci struct fib_result *res, int fib_flags) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 14018c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 14028c2ecf20Sopenharmony_ci struct trie_use_stats __percpu *stats = t->stats; 14038c2ecf20Sopenharmony_ci#endif 14048c2ecf20Sopenharmony_ci const t_key key = ntohl(flp->daddr); 14058c2ecf20Sopenharmony_ci struct key_vector *n, *pn; 14068c2ecf20Sopenharmony_ci struct fib_alias *fa; 14078c2ecf20Sopenharmony_ci unsigned long index; 14088c2ecf20Sopenharmony_ci t_key cindex; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci pn = t->kv; 14118c2ecf20Sopenharmony_ci cindex = 0; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci n = get_child_rcu(pn, cindex); 14148c2ecf20Sopenharmony_ci if (!n) { 14158c2ecf20Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, NULL, -EAGAIN); 14168c2ecf20Sopenharmony_ci return -EAGAIN; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 14208c2ecf20Sopenharmony_ci this_cpu_inc(stats->gets); 14218c2ecf20Sopenharmony_ci#endif 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* Step 1: Travel to the longest prefix match in the trie */ 14248c2ecf20Sopenharmony_ci for (;;) { 14258c2ecf20Sopenharmony_ci index = get_cindex(key, n); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci /* This bit of code is a bit tricky but it combines multiple 14288c2ecf20Sopenharmony_ci * checks into a single check. The prefix consists of the 14298c2ecf20Sopenharmony_ci * prefix plus zeros for the "bits" in the prefix. The index 14308c2ecf20Sopenharmony_ci * is the difference between the key and this value. From 14318c2ecf20Sopenharmony_ci * this we can actually derive several pieces of data. 14328c2ecf20Sopenharmony_ci * if (index >= (1ul << bits)) 14338c2ecf20Sopenharmony_ci * we have a mismatch in skip bits and failed 14348c2ecf20Sopenharmony_ci * else 14358c2ecf20Sopenharmony_ci * we know the value is cindex 14368c2ecf20Sopenharmony_ci * 14378c2ecf20Sopenharmony_ci * This check is safe even if bits == KEYLENGTH due to the 14388c2ecf20Sopenharmony_ci * fact that we can only allocate a node with 32 bits if a 14398c2ecf20Sopenharmony_ci * long is greater than 32 bits. 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_ci if (index >= (1ul << n->bits)) 14428c2ecf20Sopenharmony_ci break; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* we have found a leaf. Prefixes have already been compared */ 14458c2ecf20Sopenharmony_ci if (IS_LEAF(n)) 14468c2ecf20Sopenharmony_ci goto found; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* only record pn and cindex if we are going to be chopping 14498c2ecf20Sopenharmony_ci * bits later. Otherwise we are just wasting cycles. 14508c2ecf20Sopenharmony_ci */ 14518c2ecf20Sopenharmony_ci if (n->slen > n->pos) { 14528c2ecf20Sopenharmony_ci pn = n; 14538c2ecf20Sopenharmony_ci cindex = index; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci n = get_child_rcu(n, index); 14578c2ecf20Sopenharmony_ci if (unlikely(!n)) 14588c2ecf20Sopenharmony_ci goto backtrace; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci /* Step 2: Sort out leaves and begin backtracing for longest prefix */ 14628c2ecf20Sopenharmony_ci for (;;) { 14638c2ecf20Sopenharmony_ci /* record the pointer where our next node pointer is stored */ 14648c2ecf20Sopenharmony_ci struct key_vector __rcu **cptr = n->tnode; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci /* This test verifies that none of the bits that differ 14678c2ecf20Sopenharmony_ci * between the key and the prefix exist in the region of 14688c2ecf20Sopenharmony_ci * the lsb and higher in the prefix. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_ci if (unlikely(prefix_mismatch(key, n)) || (n->slen == n->pos)) 14718c2ecf20Sopenharmony_ci goto backtrace; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* exit out and process leaf */ 14748c2ecf20Sopenharmony_ci if (unlikely(IS_LEAF(n))) 14758c2ecf20Sopenharmony_ci break; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci /* Don't bother recording parent info. Since we are in 14788c2ecf20Sopenharmony_ci * prefix match mode we will have to come back to wherever 14798c2ecf20Sopenharmony_ci * we started this traversal anyway 14808c2ecf20Sopenharmony_ci */ 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci while ((n = rcu_dereference(*cptr)) == NULL) { 14838c2ecf20Sopenharmony_cibacktrace: 14848c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 14858c2ecf20Sopenharmony_ci if (!n) 14868c2ecf20Sopenharmony_ci this_cpu_inc(stats->null_node_hit); 14878c2ecf20Sopenharmony_ci#endif 14888c2ecf20Sopenharmony_ci /* If we are at cindex 0 there are no more bits for 14898c2ecf20Sopenharmony_ci * us to strip at this level so we must ascend back 14908c2ecf20Sopenharmony_ci * up one level to see if there are any more bits to 14918c2ecf20Sopenharmony_ci * be stripped there. 14928c2ecf20Sopenharmony_ci */ 14938c2ecf20Sopenharmony_ci while (!cindex) { 14948c2ecf20Sopenharmony_ci t_key pkey = pn->key; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* If we don't have a parent then there is 14978c2ecf20Sopenharmony_ci * nothing for us to do as we do not have any 14988c2ecf20Sopenharmony_ci * further nodes to parse. 14998c2ecf20Sopenharmony_ci */ 15008c2ecf20Sopenharmony_ci if (IS_TRIE(pn)) { 15018c2ecf20Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, 15028c2ecf20Sopenharmony_ci NULL, -EAGAIN); 15038c2ecf20Sopenharmony_ci return -EAGAIN; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 15068c2ecf20Sopenharmony_ci this_cpu_inc(stats->backtrack); 15078c2ecf20Sopenharmony_ci#endif 15088c2ecf20Sopenharmony_ci /* Get Child's index */ 15098c2ecf20Sopenharmony_ci pn = node_parent_rcu(pn); 15108c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn); 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* strip the least significant bit from the cindex */ 15148c2ecf20Sopenharmony_ci cindex &= cindex - 1; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci /* grab pointer for next child node */ 15178c2ecf20Sopenharmony_ci cptr = &pn->tnode[cindex]; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cifound: 15228c2ecf20Sopenharmony_ci /* this line carries forward the xor from earlier in the function */ 15238c2ecf20Sopenharmony_ci index = key ^ n->key; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* Step 3: Process the leaf, if that fails fall back to backtracing */ 15268c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) { 15278c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 15288c2ecf20Sopenharmony_ci struct fib_nh_common *nhc; 15298c2ecf20Sopenharmony_ci int nhsel, err; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if ((BITS_PER_LONG > KEYLENGTH) || (fa->fa_slen < KEYLENGTH)) { 15328c2ecf20Sopenharmony_ci if (index >= (1ul << fa->fa_slen)) 15338c2ecf20Sopenharmony_ci continue; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) 15368c2ecf20Sopenharmony_ci continue; 15378c2ecf20Sopenharmony_ci /* Paired with WRITE_ONCE() in fib_release_info() */ 15388c2ecf20Sopenharmony_ci if (READ_ONCE(fi->fib_dead)) 15398c2ecf20Sopenharmony_ci continue; 15408c2ecf20Sopenharmony_ci if (fa->fa_info->fib_scope < flp->flowi4_scope) 15418c2ecf20Sopenharmony_ci continue; 15428c2ecf20Sopenharmony_ci fib_alias_accessed(fa); 15438c2ecf20Sopenharmony_ci err = fib_props[fa->fa_type].error; 15448c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 15458c2ecf20Sopenharmony_ciout_reject: 15468c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 15478c2ecf20Sopenharmony_ci this_cpu_inc(stats->semantic_match_passed); 15488c2ecf20Sopenharmony_ci#endif 15498c2ecf20Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, NULL, err); 15508c2ecf20Sopenharmony_ci return err; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci if (fi->fib_flags & RTNH_F_DEAD) 15538c2ecf20Sopenharmony_ci continue; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (unlikely(fi->nh)) { 15568c2ecf20Sopenharmony_ci if (nexthop_is_blackhole(fi->nh)) { 15578c2ecf20Sopenharmony_ci err = fib_props[RTN_BLACKHOLE].error; 15588c2ecf20Sopenharmony_ci goto out_reject; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci nhc = nexthop_get_nhc_lookup(fi->nh, fib_flags, flp, 15628c2ecf20Sopenharmony_ci &nhsel); 15638c2ecf20Sopenharmony_ci if (nhc) 15648c2ecf20Sopenharmony_ci goto set_result; 15658c2ecf20Sopenharmony_ci goto miss; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) { 15698c2ecf20Sopenharmony_ci nhc = fib_info_nhc(fi, nhsel); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (!fib_lookup_good_nhc(nhc, fib_flags, flp)) 15728c2ecf20Sopenharmony_ci continue; 15738c2ecf20Sopenharmony_ciset_result: 15748c2ecf20Sopenharmony_ci if (!(fib_flags & FIB_LOOKUP_NOREF)) 15758c2ecf20Sopenharmony_ci refcount_inc(&fi->fib_clntref); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci res->prefix = htonl(n->key); 15788c2ecf20Sopenharmony_ci res->prefixlen = KEYLENGTH - fa->fa_slen; 15798c2ecf20Sopenharmony_ci res->nh_sel = nhsel; 15808c2ecf20Sopenharmony_ci res->nhc = nhc; 15818c2ecf20Sopenharmony_ci res->type = fa->fa_type; 15828c2ecf20Sopenharmony_ci res->scope = fi->fib_scope; 15838c2ecf20Sopenharmony_ci res->fi = fi; 15848c2ecf20Sopenharmony_ci res->table = tb; 15858c2ecf20Sopenharmony_ci res->fa_head = &n->leaf; 15868c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 15878c2ecf20Sopenharmony_ci this_cpu_inc(stats->semantic_match_passed); 15888c2ecf20Sopenharmony_ci#endif 15898c2ecf20Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, nhc, err); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci return err; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_cimiss: 15958c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 15968c2ecf20Sopenharmony_ci this_cpu_inc(stats->semantic_match_miss); 15978c2ecf20Sopenharmony_ci#endif 15988c2ecf20Sopenharmony_ci goto backtrace; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_table_lookup); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp, 16038c2ecf20Sopenharmony_ci struct key_vector *l, struct fib_alias *old) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci /* record the location of the previous list_info entry */ 16068c2ecf20Sopenharmony_ci struct hlist_node **pprev = old->fa_list.pprev; 16078c2ecf20Sopenharmony_ci struct fib_alias *fa = hlist_entry(pprev, typeof(*fa), fa_list.next); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* remove the fib_alias from the list */ 16108c2ecf20Sopenharmony_ci hlist_del_rcu(&old->fa_list); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* if we emptied the list this leaf will be freed and we can sort 16138c2ecf20Sopenharmony_ci * out parent suffix lengths as a part of trie_rebalance 16148c2ecf20Sopenharmony_ci */ 16158c2ecf20Sopenharmony_ci if (hlist_empty(&l->leaf)) { 16168c2ecf20Sopenharmony_ci if (tp->slen == l->slen) 16178c2ecf20Sopenharmony_ci node_pull_suffix(tp, tp->pos); 16188c2ecf20Sopenharmony_ci put_child_root(tp, l->key, NULL); 16198c2ecf20Sopenharmony_ci node_free(l); 16208c2ecf20Sopenharmony_ci trie_rebalance(t, tp); 16218c2ecf20Sopenharmony_ci return; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* only access fa if it is pointing at the last valid hlist_node */ 16258c2ecf20Sopenharmony_ci if (*pprev) 16268c2ecf20Sopenharmony_ci return; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci /* update the trie with the latest suffix length */ 16298c2ecf20Sopenharmony_ci l->slen = fa->fa_slen; 16308c2ecf20Sopenharmony_ci node_pull_suffix(tp, fa->fa_slen); 16318c2ecf20Sopenharmony_ci} 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic void fib_notify_alias_delete(struct net *net, u32 key, 16348c2ecf20Sopenharmony_ci struct hlist_head *fah, 16358c2ecf20Sopenharmony_ci struct fib_alias *fa_to_delete, 16368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci struct fib_alias *fa_next, *fa_to_notify; 16398c2ecf20Sopenharmony_ci u32 tb_id = fa_to_delete->tb_id; 16408c2ecf20Sopenharmony_ci u8 slen = fa_to_delete->fa_slen; 16418c2ecf20Sopenharmony_ci enum fib_event_type fib_event; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* Do not notify if we do not care about the route. */ 16448c2ecf20Sopenharmony_ci if (fib_find_alias(fah, slen, 0, 0, tb_id, true) != fa_to_delete) 16458c2ecf20Sopenharmony_ci return; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Determine if the route should be replaced by the next route in the 16488c2ecf20Sopenharmony_ci * list. 16498c2ecf20Sopenharmony_ci */ 16508c2ecf20Sopenharmony_ci fa_next = hlist_entry_safe(fa_to_delete->fa_list.next, 16518c2ecf20Sopenharmony_ci struct fib_alias, fa_list); 16528c2ecf20Sopenharmony_ci if (fa_next && fa_next->fa_slen == slen && fa_next->tb_id == tb_id) { 16538c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 16548c2ecf20Sopenharmony_ci fa_to_notify = fa_next; 16558c2ecf20Sopenharmony_ci } else { 16568c2ecf20Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_DEL; 16578c2ecf20Sopenharmony_ci fa_to_notify = fa_to_delete; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci call_fib_entry_notifiers(net, fib_event, key, KEYLENGTH - slen, 16608c2ecf20Sopenharmony_ci fa_to_notify, extack); 16618c2ecf20Sopenharmony_ci} 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */ 16648c2ecf20Sopenharmony_ciint fib_table_delete(struct net *net, struct fib_table *tb, 16658c2ecf20Sopenharmony_ci struct fib_config *cfg, struct netlink_ext_ack *extack) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 16688c2ecf20Sopenharmony_ci struct fib_alias *fa, *fa_to_delete; 16698c2ecf20Sopenharmony_ci struct key_vector *l, *tp; 16708c2ecf20Sopenharmony_ci u8 plen = cfg->fc_dst_len; 16718c2ecf20Sopenharmony_ci u8 slen = KEYLENGTH - plen; 16728c2ecf20Sopenharmony_ci u8 tos = cfg->fc_tos; 16738c2ecf20Sopenharmony_ci u32 key; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci key = ntohl(cfg->fc_dst); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (!fib_valid_key_len(key, plen, extack)) 16788c2ecf20Sopenharmony_ci return -EINVAL; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci l = fib_find_node(t, &tp, key); 16818c2ecf20Sopenharmony_ci if (!l) 16828c2ecf20Sopenharmony_ci return -ESRCH; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id, false); 16858c2ecf20Sopenharmony_ci if (!fa) 16868c2ecf20Sopenharmony_ci return -ESRCH; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci fa_to_delete = NULL; 16918c2ecf20Sopenharmony_ci hlist_for_each_entry_from(fa, fa_list) { 16928c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if ((fa->fa_slen != slen) || 16958c2ecf20Sopenharmony_ci (fa->tb_id != tb->tb_id) || 16968c2ecf20Sopenharmony_ci (fa->fa_tos != tos)) 16978c2ecf20Sopenharmony_ci break; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && 17008c2ecf20Sopenharmony_ci (cfg->fc_scope == RT_SCOPE_NOWHERE || 17018c2ecf20Sopenharmony_ci fa->fa_info->fib_scope == cfg->fc_scope) && 17028c2ecf20Sopenharmony_ci (!cfg->fc_prefsrc || 17038c2ecf20Sopenharmony_ci fi->fib_prefsrc == cfg->fc_prefsrc) && 17048c2ecf20Sopenharmony_ci (!cfg->fc_protocol || 17058c2ecf20Sopenharmony_ci fi->fib_protocol == cfg->fc_protocol) && 17068c2ecf20Sopenharmony_ci fib_nh_match(net, cfg, fi, extack) == 0 && 17078c2ecf20Sopenharmony_ci fib_metrics_match(cfg, fi)) { 17088c2ecf20Sopenharmony_ci fa_to_delete = fa; 17098c2ecf20Sopenharmony_ci break; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!fa_to_delete) 17148c2ecf20Sopenharmony_ci return -ESRCH; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci fib_notify_alias_delete(net, key, &l->leaf, fa_to_delete, extack); 17178c2ecf20Sopenharmony_ci rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, 17188c2ecf20Sopenharmony_ci &cfg->fc_nlinfo, 0); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci if (!plen) 17218c2ecf20Sopenharmony_ci tb->tb_num_default--; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci fib_remove_alias(t, tp, l, fa_to_delete); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (fa_to_delete->fa_state & FA_S_ACCESSED) 17268c2ecf20Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci fib_release_info(fa_to_delete->fa_info); 17298c2ecf20Sopenharmony_ci alias_free_mem_rcu(fa_to_delete); 17308c2ecf20Sopenharmony_ci return 0; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci/* Scan for the next leaf starting at the provided key value */ 17348c2ecf20Sopenharmony_cistatic struct key_vector *leaf_walk_rcu(struct key_vector **tn, t_key key) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci struct key_vector *pn, *n = *tn; 17378c2ecf20Sopenharmony_ci unsigned long cindex; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci /* this loop is meant to try and find the key in the trie */ 17408c2ecf20Sopenharmony_ci do { 17418c2ecf20Sopenharmony_ci /* record parent and next child index */ 17428c2ecf20Sopenharmony_ci pn = n; 17438c2ecf20Sopenharmony_ci cindex = (key > pn->key) ? get_index(key, pn) : 0; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (cindex >> pn->bits) 17468c2ecf20Sopenharmony_ci break; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci /* descend into the next child */ 17498c2ecf20Sopenharmony_ci n = get_child_rcu(pn, cindex++); 17508c2ecf20Sopenharmony_ci if (!n) 17518c2ecf20Sopenharmony_ci break; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* guarantee forward progress on the keys */ 17548c2ecf20Sopenharmony_ci if (IS_LEAF(n) && (n->key >= key)) 17558c2ecf20Sopenharmony_ci goto found; 17568c2ecf20Sopenharmony_ci } while (IS_TNODE(n)); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci /* this loop will search for the next leaf with a greater key */ 17598c2ecf20Sopenharmony_ci while (!IS_TRIE(pn)) { 17608c2ecf20Sopenharmony_ci /* if we exhausted the parent node we will need to climb */ 17618c2ecf20Sopenharmony_ci if (cindex >= (1ul << pn->bits)) { 17628c2ecf20Sopenharmony_ci t_key pkey = pn->key; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci pn = node_parent_rcu(pn); 17658c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn) + 1; 17668c2ecf20Sopenharmony_ci continue; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci /* grab the next available node */ 17708c2ecf20Sopenharmony_ci n = get_child_rcu(pn, cindex++); 17718c2ecf20Sopenharmony_ci if (!n) 17728c2ecf20Sopenharmony_ci continue; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* no need to compare keys since we bumped the index */ 17758c2ecf20Sopenharmony_ci if (IS_LEAF(n)) 17768c2ecf20Sopenharmony_ci goto found; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* Rescan start scanning in new node */ 17798c2ecf20Sopenharmony_ci pn = n; 17808c2ecf20Sopenharmony_ci cindex = 0; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci *tn = pn; 17848c2ecf20Sopenharmony_ci return NULL; /* Root of trie */ 17858c2ecf20Sopenharmony_cifound: 17868c2ecf20Sopenharmony_ci /* if we are at the limit for keys just return NULL for the tnode */ 17878c2ecf20Sopenharmony_ci *tn = pn; 17888c2ecf20Sopenharmony_ci return n; 17898c2ecf20Sopenharmony_ci} 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_cistatic void fib_trie_free(struct fib_table *tb) 17928c2ecf20Sopenharmony_ci{ 17938c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 17948c2ecf20Sopenharmony_ci struct key_vector *pn = t->kv; 17958c2ecf20Sopenharmony_ci unsigned long cindex = 1; 17968c2ecf20Sopenharmony_ci struct hlist_node *tmp; 17978c2ecf20Sopenharmony_ci struct fib_alias *fa; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* walk trie in reverse order and free everything */ 18008c2ecf20Sopenharmony_ci for (;;) { 18018c2ecf20Sopenharmony_ci struct key_vector *n; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (!(cindex--)) { 18048c2ecf20Sopenharmony_ci t_key pkey = pn->key; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (IS_TRIE(pn)) 18078c2ecf20Sopenharmony_ci break; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci n = pn; 18108c2ecf20Sopenharmony_ci pn = node_parent(pn); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* drop emptied tnode */ 18138c2ecf20Sopenharmony_ci put_child_root(pn, n->key, NULL); 18148c2ecf20Sopenharmony_ci node_free(n); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci continue; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* grab the next available node */ 18228c2ecf20Sopenharmony_ci n = get_child(pn, cindex); 18238c2ecf20Sopenharmony_ci if (!n) 18248c2ecf20Sopenharmony_ci continue; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 18278c2ecf20Sopenharmony_ci /* record pn and cindex for leaf walking */ 18288c2ecf20Sopenharmony_ci pn = n; 18298c2ecf20Sopenharmony_ci cindex = 1ul << n->bits; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci continue; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 18358c2ecf20Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 18368c2ecf20Sopenharmony_ci alias_free_mem_rcu(fa); 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci put_child_root(pn, n->key, NULL); 18408c2ecf20Sopenharmony_ci node_free(n); 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 18448c2ecf20Sopenharmony_ci free_percpu(t->stats); 18458c2ecf20Sopenharmony_ci#endif 18468c2ecf20Sopenharmony_ci kfree(tb); 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_cistruct fib_table *fib_trie_unmerge(struct fib_table *oldtb) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci struct trie *ot = (struct trie *)oldtb->tb_data; 18528c2ecf20Sopenharmony_ci struct key_vector *l, *tp = ot->kv; 18538c2ecf20Sopenharmony_ci struct fib_table *local_tb; 18548c2ecf20Sopenharmony_ci struct fib_alias *fa; 18558c2ecf20Sopenharmony_ci struct trie *lt; 18568c2ecf20Sopenharmony_ci t_key key = 0; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci if (oldtb->tb_data == oldtb->__data) 18598c2ecf20Sopenharmony_ci return oldtb; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci local_tb = fib_trie_table(RT_TABLE_LOCAL, NULL); 18628c2ecf20Sopenharmony_ci if (!local_tb) 18638c2ecf20Sopenharmony_ci return NULL; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci lt = (struct trie *)local_tb->tb_data; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 18688c2ecf20Sopenharmony_ci struct key_vector *local_l = NULL, *local_tp; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci hlist_for_each_entry(fa, &l->leaf, fa_list) { 18718c2ecf20Sopenharmony_ci struct fib_alias *new_fa; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (local_tb->tb_id != fa->tb_id) 18748c2ecf20Sopenharmony_ci continue; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci /* clone fa for new local table */ 18778c2ecf20Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 18788c2ecf20Sopenharmony_ci if (!new_fa) 18798c2ecf20Sopenharmony_ci goto out; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci memcpy(new_fa, fa, sizeof(*fa)); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci /* insert clone into table */ 18848c2ecf20Sopenharmony_ci if (!local_l) 18858c2ecf20Sopenharmony_ci local_l = fib_find_node(lt, &local_tp, l->key); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (fib_insert_alias(lt, local_tp, local_l, new_fa, 18888c2ecf20Sopenharmony_ci NULL, l->key)) { 18898c2ecf20Sopenharmony_ci kmem_cache_free(fn_alias_kmem, new_fa); 18908c2ecf20Sopenharmony_ci goto out; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci /* stop loop if key wrapped back to 0 */ 18958c2ecf20Sopenharmony_ci key = l->key + 1; 18968c2ecf20Sopenharmony_ci if (key < l->key) 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci return local_tb; 19018c2ecf20Sopenharmony_ciout: 19028c2ecf20Sopenharmony_ci fib_trie_free(local_tb); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci return NULL; 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci/* Caller must hold RTNL */ 19088c2ecf20Sopenharmony_civoid fib_table_flush_external(struct fib_table *tb) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 19118c2ecf20Sopenharmony_ci struct key_vector *pn = t->kv; 19128c2ecf20Sopenharmony_ci unsigned long cindex = 1; 19138c2ecf20Sopenharmony_ci struct hlist_node *tmp; 19148c2ecf20Sopenharmony_ci struct fib_alias *fa; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci /* walk trie in reverse order */ 19178c2ecf20Sopenharmony_ci for (;;) { 19188c2ecf20Sopenharmony_ci unsigned char slen = 0; 19198c2ecf20Sopenharmony_ci struct key_vector *n; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci if (!(cindex--)) { 19228c2ecf20Sopenharmony_ci t_key pkey = pn->key; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci /* cannot resize the trie vector */ 19258c2ecf20Sopenharmony_ci if (IS_TRIE(pn)) 19268c2ecf20Sopenharmony_ci break; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* update the suffix to address pulled leaves */ 19298c2ecf20Sopenharmony_ci if (pn->slen > pn->pos) 19308c2ecf20Sopenharmony_ci update_suffix(pn); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* resize completed node */ 19338c2ecf20Sopenharmony_ci pn = resize(t, pn); 19348c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci continue; 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci /* grab the next available node */ 19408c2ecf20Sopenharmony_ci n = get_child(pn, cindex); 19418c2ecf20Sopenharmony_ci if (!n) 19428c2ecf20Sopenharmony_ci continue; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 19458c2ecf20Sopenharmony_ci /* record pn and cindex for leaf walking */ 19468c2ecf20Sopenharmony_ci pn = n; 19478c2ecf20Sopenharmony_ci cindex = 1ul << n->bits; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci continue; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 19538c2ecf20Sopenharmony_ci /* if alias was cloned to local then we just 19548c2ecf20Sopenharmony_ci * need to remove the local copy from main 19558c2ecf20Sopenharmony_ci */ 19568c2ecf20Sopenharmony_ci if (tb->tb_id != fa->tb_id) { 19578c2ecf20Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 19588c2ecf20Sopenharmony_ci alias_free_mem_rcu(fa); 19598c2ecf20Sopenharmony_ci continue; 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci /* record local slen */ 19638c2ecf20Sopenharmony_ci slen = fa->fa_slen; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci /* update leaf slen */ 19678c2ecf20Sopenharmony_ci n->slen = slen; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (hlist_empty(&n->leaf)) { 19708c2ecf20Sopenharmony_ci put_child_root(pn, n->key, NULL); 19718c2ecf20Sopenharmony_ci node_free(n); 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci} 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */ 19778c2ecf20Sopenharmony_ciint fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 19808c2ecf20Sopenharmony_ci struct nl_info info = { .nl_net = net }; 19818c2ecf20Sopenharmony_ci struct key_vector *pn = t->kv; 19828c2ecf20Sopenharmony_ci unsigned long cindex = 1; 19838c2ecf20Sopenharmony_ci struct hlist_node *tmp; 19848c2ecf20Sopenharmony_ci struct fib_alias *fa; 19858c2ecf20Sopenharmony_ci int found = 0; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* walk trie in reverse order */ 19888c2ecf20Sopenharmony_ci for (;;) { 19898c2ecf20Sopenharmony_ci unsigned char slen = 0; 19908c2ecf20Sopenharmony_ci struct key_vector *n; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (!(cindex--)) { 19938c2ecf20Sopenharmony_ci t_key pkey = pn->key; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci /* cannot resize the trie vector */ 19968c2ecf20Sopenharmony_ci if (IS_TRIE(pn)) 19978c2ecf20Sopenharmony_ci break; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci /* update the suffix to address pulled leaves */ 20008c2ecf20Sopenharmony_ci if (pn->slen > pn->pos) 20018c2ecf20Sopenharmony_ci update_suffix(pn); 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* resize completed node */ 20048c2ecf20Sopenharmony_ci pn = resize(t, pn); 20058c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci continue; 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci /* grab the next available node */ 20118c2ecf20Sopenharmony_ci n = get_child(pn, cindex); 20128c2ecf20Sopenharmony_ci if (!n) 20138c2ecf20Sopenharmony_ci continue; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 20168c2ecf20Sopenharmony_ci /* record pn and cindex for leaf walking */ 20178c2ecf20Sopenharmony_ci pn = n; 20188c2ecf20Sopenharmony_ci cindex = 1ul << n->bits; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci continue; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 20248c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (!fi || tb->tb_id != fa->tb_id || 20278c2ecf20Sopenharmony_ci (!(fi->fib_flags & RTNH_F_DEAD) && 20288c2ecf20Sopenharmony_ci !fib_props[fa->fa_type].error)) { 20298c2ecf20Sopenharmony_ci slen = fa->fa_slen; 20308c2ecf20Sopenharmony_ci continue; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci /* Do not flush error routes if network namespace is 20348c2ecf20Sopenharmony_ci * not being dismantled 20358c2ecf20Sopenharmony_ci */ 20368c2ecf20Sopenharmony_ci if (!flush_all && fib_props[fa->fa_type].error) { 20378c2ecf20Sopenharmony_ci slen = fa->fa_slen; 20388c2ecf20Sopenharmony_ci continue; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci fib_notify_alias_delete(net, n->key, &n->leaf, fa, 20428c2ecf20Sopenharmony_ci NULL); 20438c2ecf20Sopenharmony_ci if (fi->pfsrc_removed) 20448c2ecf20Sopenharmony_ci rtmsg_fib(RTM_DELROUTE, htonl(n->key), fa, 20458c2ecf20Sopenharmony_ci KEYLENGTH - fa->fa_slen, tb->tb_id, &info, 0); 20468c2ecf20Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 20478c2ecf20Sopenharmony_ci fib_release_info(fa->fa_info); 20488c2ecf20Sopenharmony_ci alias_free_mem_rcu(fa); 20498c2ecf20Sopenharmony_ci found++; 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci /* update leaf slen */ 20538c2ecf20Sopenharmony_ci n->slen = slen; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (hlist_empty(&n->leaf)) { 20568c2ecf20Sopenharmony_ci put_child_root(pn, n->key, NULL); 20578c2ecf20Sopenharmony_ci node_free(n); 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci pr_debug("trie_flush found=%d\n", found); 20628c2ecf20Sopenharmony_ci return found; 20638c2ecf20Sopenharmony_ci} 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci/* derived from fib_trie_free */ 20668c2ecf20Sopenharmony_cistatic void __fib_info_notify_update(struct net *net, struct fib_table *tb, 20678c2ecf20Sopenharmony_ci struct nl_info *info) 20688c2ecf20Sopenharmony_ci{ 20698c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 20708c2ecf20Sopenharmony_ci struct key_vector *pn = t->kv; 20718c2ecf20Sopenharmony_ci unsigned long cindex = 1; 20728c2ecf20Sopenharmony_ci struct fib_alias *fa; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci for (;;) { 20758c2ecf20Sopenharmony_ci struct key_vector *n; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci if (!(cindex--)) { 20788c2ecf20Sopenharmony_ci t_key pkey = pn->key; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci if (IS_TRIE(pn)) 20818c2ecf20Sopenharmony_ci break; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci pn = node_parent(pn); 20848c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn); 20858c2ecf20Sopenharmony_ci continue; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci /* grab the next available node */ 20898c2ecf20Sopenharmony_ci n = get_child(pn, cindex); 20908c2ecf20Sopenharmony_ci if (!n) 20918c2ecf20Sopenharmony_ci continue; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 20948c2ecf20Sopenharmony_ci /* record pn and cindex for leaf walking */ 20958c2ecf20Sopenharmony_ci pn = n; 20968c2ecf20Sopenharmony_ci cindex = 1ul << n->bits; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci continue; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci hlist_for_each_entry(fa, &n->leaf, fa_list) { 21028c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (!fi || !fi->nh_updated || fa->tb_id != tb->tb_id) 21058c2ecf20Sopenharmony_ci continue; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(n->key), fa, 21088c2ecf20Sopenharmony_ci KEYLENGTH - fa->fa_slen, tb->tb_id, 21098c2ecf20Sopenharmony_ci info, NLM_F_REPLACE); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* call_fib_entry_notifiers will be removed when 21128c2ecf20Sopenharmony_ci * in-kernel notifier is implemented and supported 21138c2ecf20Sopenharmony_ci * for nexthop objects 21148c2ecf20Sopenharmony_ci */ 21158c2ecf20Sopenharmony_ci call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, 21168c2ecf20Sopenharmony_ci n->key, 21178c2ecf20Sopenharmony_ci KEYLENGTH - fa->fa_slen, fa, 21188c2ecf20Sopenharmony_ci NULL); 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci} 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_civoid fib_info_notify_update(struct net *net, struct nl_info *info) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci unsigned int h; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 21288c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 21298c2ecf20Sopenharmony_ci struct fib_table *tb; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist, 21328c2ecf20Sopenharmony_ci lockdep_rtnl_is_held()) 21338c2ecf20Sopenharmony_ci __fib_info_notify_update(net, tb, info); 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_cistatic int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, 21388c2ecf20Sopenharmony_ci struct notifier_block *nb, 21398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 21408c2ecf20Sopenharmony_ci{ 21418c2ecf20Sopenharmony_ci struct fib_alias *fa; 21428c2ecf20Sopenharmony_ci int last_slen = -1; 21438c2ecf20Sopenharmony_ci int err; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 21468c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci if (!fi) 21498c2ecf20Sopenharmony_ci continue; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci /* local and main table can share the same trie, 21528c2ecf20Sopenharmony_ci * so don't notify twice for the same entry. 21538c2ecf20Sopenharmony_ci */ 21548c2ecf20Sopenharmony_ci if (tb->tb_id != fa->tb_id) 21558c2ecf20Sopenharmony_ci continue; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (fa->fa_slen == last_slen) 21588c2ecf20Sopenharmony_ci continue; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci last_slen = fa->fa_slen; 21618c2ecf20Sopenharmony_ci err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE, 21628c2ecf20Sopenharmony_ci l->key, KEYLENGTH - fa->fa_slen, 21638c2ecf20Sopenharmony_ci fa, extack); 21648c2ecf20Sopenharmony_ci if (err) 21658c2ecf20Sopenharmony_ci return err; 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci return 0; 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_cistatic int fib_table_notify(struct fib_table *tb, struct notifier_block *nb, 21718c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 21728c2ecf20Sopenharmony_ci{ 21738c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 21748c2ecf20Sopenharmony_ci struct key_vector *l, *tp = t->kv; 21758c2ecf20Sopenharmony_ci t_key key = 0; 21768c2ecf20Sopenharmony_ci int err; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 21798c2ecf20Sopenharmony_ci err = fib_leaf_notify(l, tb, nb, extack); 21808c2ecf20Sopenharmony_ci if (err) 21818c2ecf20Sopenharmony_ci return err; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci key = l->key + 1; 21848c2ecf20Sopenharmony_ci /* stop in case of wrap around */ 21858c2ecf20Sopenharmony_ci if (key < l->key) 21868c2ecf20Sopenharmony_ci break; 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci return 0; 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ciint fib_notify(struct net *net, struct notifier_block *nb, 21928c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci unsigned int h; 21958c2ecf20Sopenharmony_ci int err; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 21988c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 21998c2ecf20Sopenharmony_ci struct fib_table *tb; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 22028c2ecf20Sopenharmony_ci err = fib_table_notify(tb, nb, extack); 22038c2ecf20Sopenharmony_ci if (err) 22048c2ecf20Sopenharmony_ci return err; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci } 22078c2ecf20Sopenharmony_ci return 0; 22088c2ecf20Sopenharmony_ci} 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_cistatic void __trie_free_rcu(struct rcu_head *head) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci struct fib_table *tb = container_of(head, struct fib_table, rcu); 22138c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 22148c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci if (tb->tb_data == tb->__data) 22178c2ecf20Sopenharmony_ci free_percpu(t->stats); 22188c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_FIB_TRIE_STATS */ 22198c2ecf20Sopenharmony_ci kfree(tb); 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_civoid fib_free_table(struct fib_table *tb) 22238c2ecf20Sopenharmony_ci{ 22248c2ecf20Sopenharmony_ci call_rcu(&tb->rcu, __trie_free_rcu); 22258c2ecf20Sopenharmony_ci} 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_cistatic int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb, 22288c2ecf20Sopenharmony_ci struct sk_buff *skb, struct netlink_callback *cb, 22298c2ecf20Sopenharmony_ci struct fib_dump_filter *filter) 22308c2ecf20Sopenharmony_ci{ 22318c2ecf20Sopenharmony_ci unsigned int flags = NLM_F_MULTI; 22328c2ecf20Sopenharmony_ci __be32 xkey = htonl(l->key); 22338c2ecf20Sopenharmony_ci int i, s_i, i_fa, s_fa, err; 22348c2ecf20Sopenharmony_ci struct fib_alias *fa; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (filter->filter_set || 22378c2ecf20Sopenharmony_ci !filter->dump_exceptions || !filter->dump_routes) 22388c2ecf20Sopenharmony_ci flags |= NLM_F_DUMP_FILTERED; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci s_i = cb->args[4]; 22418c2ecf20Sopenharmony_ci s_fa = cb->args[5]; 22428c2ecf20Sopenharmony_ci i = 0; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* rcu_read_lock is hold by caller */ 22458c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 22468c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (i < s_i) 22498c2ecf20Sopenharmony_ci goto next; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci i_fa = 0; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (tb->tb_id != fa->tb_id) 22548c2ecf20Sopenharmony_ci goto next; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci if (filter->filter_set) { 22578c2ecf20Sopenharmony_ci if (filter->rt_type && fa->fa_type != filter->rt_type) 22588c2ecf20Sopenharmony_ci goto next; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci if ((filter->protocol && 22618c2ecf20Sopenharmony_ci fi->fib_protocol != filter->protocol)) 22628c2ecf20Sopenharmony_ci goto next; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (filter->dev && 22658c2ecf20Sopenharmony_ci !fib_info_nh_uses_dev(fi, filter->dev)) 22668c2ecf20Sopenharmony_ci goto next; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci if (filter->dump_routes) { 22708c2ecf20Sopenharmony_ci if (!s_fa) { 22718c2ecf20Sopenharmony_ci struct fib_rt_info fri; 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci fri.fi = fi; 22748c2ecf20Sopenharmony_ci fri.tb_id = tb->tb_id; 22758c2ecf20Sopenharmony_ci fri.dst = xkey; 22768c2ecf20Sopenharmony_ci fri.dst_len = KEYLENGTH - fa->fa_slen; 22778c2ecf20Sopenharmony_ci fri.tos = fa->fa_tos; 22788c2ecf20Sopenharmony_ci fri.type = fa->fa_type; 22798c2ecf20Sopenharmony_ci fri.offload = fa->offload; 22808c2ecf20Sopenharmony_ci fri.trap = fa->trap; 22818c2ecf20Sopenharmony_ci err = fib_dump_info(skb, 22828c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 22838c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 22848c2ecf20Sopenharmony_ci RTM_NEWROUTE, &fri, flags); 22858c2ecf20Sopenharmony_ci if (err < 0) 22868c2ecf20Sopenharmony_ci goto stop; 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci i_fa++; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (filter->dump_exceptions) { 22938c2ecf20Sopenharmony_ci err = fib_dump_info_fnhe(skb, cb, tb->tb_id, fi, 22948c2ecf20Sopenharmony_ci &i_fa, s_fa, flags); 22958c2ecf20Sopenharmony_ci if (err < 0) 22968c2ecf20Sopenharmony_ci goto stop; 22978c2ecf20Sopenharmony_ci } 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_cinext: 23008c2ecf20Sopenharmony_ci i++; 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci cb->args[4] = i; 23048c2ecf20Sopenharmony_ci return skb->len; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_cistop: 23078c2ecf20Sopenharmony_ci cb->args[4] = i; 23088c2ecf20Sopenharmony_ci cb->args[5] = i_fa; 23098c2ecf20Sopenharmony_ci return err; 23108c2ecf20Sopenharmony_ci} 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */ 23138c2ecf20Sopenharmony_ciint fib_table_dump(struct fib_table *tb, struct sk_buff *skb, 23148c2ecf20Sopenharmony_ci struct netlink_callback *cb, struct fib_dump_filter *filter) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 23178c2ecf20Sopenharmony_ci struct key_vector *l, *tp = t->kv; 23188c2ecf20Sopenharmony_ci /* Dump starting at last key. 23198c2ecf20Sopenharmony_ci * Note: 0.0.0.0/0 (ie default) is first key. 23208c2ecf20Sopenharmony_ci */ 23218c2ecf20Sopenharmony_ci int count = cb->args[2]; 23228c2ecf20Sopenharmony_ci t_key key = cb->args[3]; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci /* First time here, count and key are both always 0. Count > 0 23258c2ecf20Sopenharmony_ci * and key == 0 means the dump has wrapped around and we are done. 23268c2ecf20Sopenharmony_ci */ 23278c2ecf20Sopenharmony_ci if (count && !key) 23288c2ecf20Sopenharmony_ci return skb->len; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 23318c2ecf20Sopenharmony_ci int err; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci err = fn_trie_dump_leaf(l, tb, skb, cb, filter); 23348c2ecf20Sopenharmony_ci if (err < 0) { 23358c2ecf20Sopenharmony_ci cb->args[3] = key; 23368c2ecf20Sopenharmony_ci cb->args[2] = count; 23378c2ecf20Sopenharmony_ci return err; 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci ++count; 23418c2ecf20Sopenharmony_ci key = l->key + 1; 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci memset(&cb->args[4], 0, 23448c2ecf20Sopenharmony_ci sizeof(cb->args) - 4*sizeof(cb->args[0])); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci /* stop loop if key wrapped back to 0 */ 23478c2ecf20Sopenharmony_ci if (key < l->key) 23488c2ecf20Sopenharmony_ci break; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci cb->args[3] = key; 23528c2ecf20Sopenharmony_ci cb->args[2] = count; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci return skb->len; 23558c2ecf20Sopenharmony_ci} 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_civoid __init fib_trie_init(void) 23588c2ecf20Sopenharmony_ci{ 23598c2ecf20Sopenharmony_ci fn_alias_kmem = kmem_cache_create("ip_fib_alias", 23608c2ecf20Sopenharmony_ci sizeof(struct fib_alias), 23618c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci trie_leaf_kmem = kmem_cache_create("ip_fib_trie", 23648c2ecf20Sopenharmony_ci LEAF_SIZE, 23658c2ecf20Sopenharmony_ci 0, SLAB_PANIC, NULL); 23668c2ecf20Sopenharmony_ci} 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_cistruct fib_table *fib_trie_table(u32 id, struct fib_table *alias) 23698c2ecf20Sopenharmony_ci{ 23708c2ecf20Sopenharmony_ci struct fib_table *tb; 23718c2ecf20Sopenharmony_ci struct trie *t; 23728c2ecf20Sopenharmony_ci size_t sz = sizeof(*tb); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci if (!alias) 23758c2ecf20Sopenharmony_ci sz += sizeof(struct trie); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci tb = kzalloc(sz, GFP_KERNEL); 23788c2ecf20Sopenharmony_ci if (!tb) 23798c2ecf20Sopenharmony_ci return NULL; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci tb->tb_id = id; 23828c2ecf20Sopenharmony_ci tb->tb_num_default = 0; 23838c2ecf20Sopenharmony_ci tb->tb_data = (alias ? alias->__data : tb->__data); 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci if (alias) 23868c2ecf20Sopenharmony_ci return tb; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci t = (struct trie *) tb->tb_data; 23898c2ecf20Sopenharmony_ci t->kv[0].pos = KEYLENGTH; 23908c2ecf20Sopenharmony_ci t->kv[0].slen = KEYLENGTH; 23918c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 23928c2ecf20Sopenharmony_ci t->stats = alloc_percpu(struct trie_use_stats); 23938c2ecf20Sopenharmony_ci if (!t->stats) { 23948c2ecf20Sopenharmony_ci kfree(tb); 23958c2ecf20Sopenharmony_ci tb = NULL; 23968c2ecf20Sopenharmony_ci } 23978c2ecf20Sopenharmony_ci#endif 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci return tb; 24008c2ecf20Sopenharmony_ci} 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 24038c2ecf20Sopenharmony_ci/* Depth first Trie walk iterator */ 24048c2ecf20Sopenharmony_cistruct fib_trie_iter { 24058c2ecf20Sopenharmony_ci struct seq_net_private p; 24068c2ecf20Sopenharmony_ci struct fib_table *tb; 24078c2ecf20Sopenharmony_ci struct key_vector *tnode; 24088c2ecf20Sopenharmony_ci unsigned int index; 24098c2ecf20Sopenharmony_ci unsigned int depth; 24108c2ecf20Sopenharmony_ci}; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_next(struct fib_trie_iter *iter) 24138c2ecf20Sopenharmony_ci{ 24148c2ecf20Sopenharmony_ci unsigned long cindex = iter->index; 24158c2ecf20Sopenharmony_ci struct key_vector *pn = iter->tnode; 24168c2ecf20Sopenharmony_ci t_key pkey; 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci pr_debug("get_next iter={node=%p index=%d depth=%d}\n", 24198c2ecf20Sopenharmony_ci iter->tnode, iter->index, iter->depth); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci while (!IS_TRIE(pn)) { 24228c2ecf20Sopenharmony_ci while (cindex < child_length(pn)) { 24238c2ecf20Sopenharmony_ci struct key_vector *n = get_child_rcu(pn, cindex++); 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci if (!n) 24268c2ecf20Sopenharmony_ci continue; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci if (IS_LEAF(n)) { 24298c2ecf20Sopenharmony_ci iter->tnode = pn; 24308c2ecf20Sopenharmony_ci iter->index = cindex; 24318c2ecf20Sopenharmony_ci } else { 24328c2ecf20Sopenharmony_ci /* push down one level */ 24338c2ecf20Sopenharmony_ci iter->tnode = n; 24348c2ecf20Sopenharmony_ci iter->index = 0; 24358c2ecf20Sopenharmony_ci ++iter->depth; 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci return n; 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci /* Current node exhausted, pop back up */ 24428c2ecf20Sopenharmony_ci pkey = pn->key; 24438c2ecf20Sopenharmony_ci pn = node_parent_rcu(pn); 24448c2ecf20Sopenharmony_ci cindex = get_index(pkey, pn) + 1; 24458c2ecf20Sopenharmony_ci --iter->depth; 24468c2ecf20Sopenharmony_ci } 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci /* record root node so further searches know we are done */ 24498c2ecf20Sopenharmony_ci iter->tnode = pn; 24508c2ecf20Sopenharmony_ci iter->index = 0; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci return NULL; 24538c2ecf20Sopenharmony_ci} 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_first(struct fib_trie_iter *iter, 24568c2ecf20Sopenharmony_ci struct trie *t) 24578c2ecf20Sopenharmony_ci{ 24588c2ecf20Sopenharmony_ci struct key_vector *n, *pn; 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci if (!t) 24618c2ecf20Sopenharmony_ci return NULL; 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci pn = t->kv; 24648c2ecf20Sopenharmony_ci n = rcu_dereference(pn->tnode[0]); 24658c2ecf20Sopenharmony_ci if (!n) 24668c2ecf20Sopenharmony_ci return NULL; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 24698c2ecf20Sopenharmony_ci iter->tnode = n; 24708c2ecf20Sopenharmony_ci iter->index = 0; 24718c2ecf20Sopenharmony_ci iter->depth = 1; 24728c2ecf20Sopenharmony_ci } else { 24738c2ecf20Sopenharmony_ci iter->tnode = pn; 24748c2ecf20Sopenharmony_ci iter->index = 0; 24758c2ecf20Sopenharmony_ci iter->depth = 0; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci return n; 24798c2ecf20Sopenharmony_ci} 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_cistatic void trie_collect_stats(struct trie *t, struct trie_stat *s) 24828c2ecf20Sopenharmony_ci{ 24838c2ecf20Sopenharmony_ci struct key_vector *n; 24848c2ecf20Sopenharmony_ci struct fib_trie_iter iter; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci memset(s, 0, sizeof(*s)); 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci rcu_read_lock(); 24898c2ecf20Sopenharmony_ci for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) { 24908c2ecf20Sopenharmony_ci if (IS_LEAF(n)) { 24918c2ecf20Sopenharmony_ci struct fib_alias *fa; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci s->leaves++; 24948c2ecf20Sopenharmony_ci s->totdepth += iter.depth; 24958c2ecf20Sopenharmony_ci if (iter.depth > s->maxdepth) 24968c2ecf20Sopenharmony_ci s->maxdepth = iter.depth; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) 24998c2ecf20Sopenharmony_ci ++s->prefixes; 25008c2ecf20Sopenharmony_ci } else { 25018c2ecf20Sopenharmony_ci s->tnodes++; 25028c2ecf20Sopenharmony_ci if (n->bits < MAX_STAT_DEPTH) 25038c2ecf20Sopenharmony_ci s->nodesizes[n->bits]++; 25048c2ecf20Sopenharmony_ci s->nullpointers += tn_info(n)->empty_children; 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci } 25078c2ecf20Sopenharmony_ci rcu_read_unlock(); 25088c2ecf20Sopenharmony_ci} 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci/* 25118c2ecf20Sopenharmony_ci * This outputs /proc/net/fib_triestats 25128c2ecf20Sopenharmony_ci */ 25138c2ecf20Sopenharmony_cistatic void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) 25148c2ecf20Sopenharmony_ci{ 25158c2ecf20Sopenharmony_ci unsigned int i, max, pointers, bytes, avdepth; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (stat->leaves) 25188c2ecf20Sopenharmony_ci avdepth = stat->totdepth*100 / stat->leaves; 25198c2ecf20Sopenharmony_ci else 25208c2ecf20Sopenharmony_ci avdepth = 0; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci seq_printf(seq, "\tAver depth: %u.%02d\n", 25238c2ecf20Sopenharmony_ci avdepth / 100, avdepth % 100); 25248c2ecf20Sopenharmony_ci seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth); 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci seq_printf(seq, "\tLeaves: %u\n", stat->leaves); 25278c2ecf20Sopenharmony_ci bytes = LEAF_SIZE * stat->leaves; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci seq_printf(seq, "\tPrefixes: %u\n", stat->prefixes); 25308c2ecf20Sopenharmony_ci bytes += sizeof(struct fib_alias) * stat->prefixes; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci seq_printf(seq, "\tInternal nodes: %u\n\t", stat->tnodes); 25338c2ecf20Sopenharmony_ci bytes += TNODE_SIZE(0) * stat->tnodes; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci max = MAX_STAT_DEPTH; 25368c2ecf20Sopenharmony_ci while (max > 0 && stat->nodesizes[max-1] == 0) 25378c2ecf20Sopenharmony_ci max--; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci pointers = 0; 25408c2ecf20Sopenharmony_ci for (i = 1; i < max; i++) 25418c2ecf20Sopenharmony_ci if (stat->nodesizes[i] != 0) { 25428c2ecf20Sopenharmony_ci seq_printf(seq, " %u: %u", i, stat->nodesizes[i]); 25438c2ecf20Sopenharmony_ci pointers += (1<<i) * stat->nodesizes[i]; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 25468c2ecf20Sopenharmony_ci seq_printf(seq, "\tPointers: %u\n", pointers); 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci bytes += sizeof(struct key_vector *) * pointers; 25498c2ecf20Sopenharmony_ci seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers); 25508c2ecf20Sopenharmony_ci seq_printf(seq, "Total size: %u kB\n", (bytes + 1023) / 1024); 25518c2ecf20Sopenharmony_ci} 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 25548c2ecf20Sopenharmony_cistatic void trie_show_usage(struct seq_file *seq, 25558c2ecf20Sopenharmony_ci const struct trie_use_stats __percpu *stats) 25568c2ecf20Sopenharmony_ci{ 25578c2ecf20Sopenharmony_ci struct trie_use_stats s = { 0 }; 25588c2ecf20Sopenharmony_ci int cpu; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci /* loop through all of the CPUs and gather up the stats */ 25618c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 25628c2ecf20Sopenharmony_ci const struct trie_use_stats *pcpu = per_cpu_ptr(stats, cpu); 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci s.gets += pcpu->gets; 25658c2ecf20Sopenharmony_ci s.backtrack += pcpu->backtrack; 25668c2ecf20Sopenharmony_ci s.semantic_match_passed += pcpu->semantic_match_passed; 25678c2ecf20Sopenharmony_ci s.semantic_match_miss += pcpu->semantic_match_miss; 25688c2ecf20Sopenharmony_ci s.null_node_hit += pcpu->null_node_hit; 25698c2ecf20Sopenharmony_ci s.resize_node_skipped += pcpu->resize_node_skipped; 25708c2ecf20Sopenharmony_ci } 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci seq_printf(seq, "\nCounters:\n---------\n"); 25738c2ecf20Sopenharmony_ci seq_printf(seq, "gets = %u\n", s.gets); 25748c2ecf20Sopenharmony_ci seq_printf(seq, "backtracks = %u\n", s.backtrack); 25758c2ecf20Sopenharmony_ci seq_printf(seq, "semantic match passed = %u\n", 25768c2ecf20Sopenharmony_ci s.semantic_match_passed); 25778c2ecf20Sopenharmony_ci seq_printf(seq, "semantic match miss = %u\n", s.semantic_match_miss); 25788c2ecf20Sopenharmony_ci seq_printf(seq, "null node hit= %u\n", s.null_node_hit); 25798c2ecf20Sopenharmony_ci seq_printf(seq, "skipped node resize = %u\n\n", s.resize_node_skipped); 25808c2ecf20Sopenharmony_ci} 25818c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_FIB_TRIE_STATS */ 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_cistatic void fib_table_print(struct seq_file *seq, struct fib_table *tb) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci if (tb->tb_id == RT_TABLE_LOCAL) 25868c2ecf20Sopenharmony_ci seq_puts(seq, "Local:\n"); 25878c2ecf20Sopenharmony_ci else if (tb->tb_id == RT_TABLE_MAIN) 25888c2ecf20Sopenharmony_ci seq_puts(seq, "Main:\n"); 25898c2ecf20Sopenharmony_ci else 25908c2ecf20Sopenharmony_ci seq_printf(seq, "Id %d:\n", tb->tb_id); 25918c2ecf20Sopenharmony_ci} 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_cistatic int fib_triestat_seq_show(struct seq_file *seq, void *v) 25958c2ecf20Sopenharmony_ci{ 25968c2ecf20Sopenharmony_ci struct net *net = (struct net *)seq->private; 25978c2ecf20Sopenharmony_ci unsigned int h; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci seq_printf(seq, 26008c2ecf20Sopenharmony_ci "Basic info: size of leaf:" 26018c2ecf20Sopenharmony_ci " %zd bytes, size of tnode: %zd bytes.\n", 26028c2ecf20Sopenharmony_ci LEAF_SIZE, TNODE_SIZE(0)); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci rcu_read_lock(); 26058c2ecf20Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 26068c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 26078c2ecf20Sopenharmony_ci struct fib_table *tb; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 26108c2ecf20Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 26118c2ecf20Sopenharmony_ci struct trie_stat stat; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci if (!t) 26148c2ecf20Sopenharmony_ci continue; 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci fib_table_print(seq, tb); 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci trie_collect_stats(t, &stat); 26198c2ecf20Sopenharmony_ci trie_show_stats(seq, &stat); 26208c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 26218c2ecf20Sopenharmony_ci trie_show_usage(seq, t->stats); 26228c2ecf20Sopenharmony_ci#endif 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci cond_resched_rcu(); 26258c2ecf20Sopenharmony_ci } 26268c2ecf20Sopenharmony_ci rcu_read_unlock(); 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci return 0; 26298c2ecf20Sopenharmony_ci} 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_idx(struct seq_file *seq, loff_t pos) 26328c2ecf20Sopenharmony_ci{ 26338c2ecf20Sopenharmony_ci struct fib_trie_iter *iter = seq->private; 26348c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 26358c2ecf20Sopenharmony_ci loff_t idx = 0; 26368c2ecf20Sopenharmony_ci unsigned int h; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 26398c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 26408c2ecf20Sopenharmony_ci struct fib_table *tb; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 26438c2ecf20Sopenharmony_ci struct key_vector *n; 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_ci for (n = fib_trie_get_first(iter, 26468c2ecf20Sopenharmony_ci (struct trie *) tb->tb_data); 26478c2ecf20Sopenharmony_ci n; n = fib_trie_get_next(iter)) 26488c2ecf20Sopenharmony_ci if (pos == idx++) { 26498c2ecf20Sopenharmony_ci iter->tb = tb; 26508c2ecf20Sopenharmony_ci return n; 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci return NULL; 26568c2ecf20Sopenharmony_ci} 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_cistatic void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos) 26598c2ecf20Sopenharmony_ci __acquires(RCU) 26608c2ecf20Sopenharmony_ci{ 26618c2ecf20Sopenharmony_ci rcu_read_lock(); 26628c2ecf20Sopenharmony_ci return fib_trie_get_idx(seq, *pos); 26638c2ecf20Sopenharmony_ci} 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_cistatic void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos) 26668c2ecf20Sopenharmony_ci{ 26678c2ecf20Sopenharmony_ci struct fib_trie_iter *iter = seq->private; 26688c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 26698c2ecf20Sopenharmony_ci struct fib_table *tb = iter->tb; 26708c2ecf20Sopenharmony_ci struct hlist_node *tb_node; 26718c2ecf20Sopenharmony_ci unsigned int h; 26728c2ecf20Sopenharmony_ci struct key_vector *n; 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci ++*pos; 26758c2ecf20Sopenharmony_ci /* next node in same table */ 26768c2ecf20Sopenharmony_ci n = fib_trie_get_next(iter); 26778c2ecf20Sopenharmony_ci if (n) 26788c2ecf20Sopenharmony_ci return n; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci /* walk rest of this hash chain */ 26818c2ecf20Sopenharmony_ci h = tb->tb_id & (FIB_TABLE_HASHSZ - 1); 26828c2ecf20Sopenharmony_ci while ((tb_node = rcu_dereference(hlist_next_rcu(&tb->tb_hlist)))) { 26838c2ecf20Sopenharmony_ci tb = hlist_entry(tb_node, struct fib_table, tb_hlist); 26848c2ecf20Sopenharmony_ci n = fib_trie_get_first(iter, (struct trie *) tb->tb_data); 26858c2ecf20Sopenharmony_ci if (n) 26868c2ecf20Sopenharmony_ci goto found; 26878c2ecf20Sopenharmony_ci } 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci /* new hash chain */ 26908c2ecf20Sopenharmony_ci while (++h < FIB_TABLE_HASHSZ) { 26918c2ecf20Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 26928c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 26938c2ecf20Sopenharmony_ci n = fib_trie_get_first(iter, (struct trie *) tb->tb_data); 26948c2ecf20Sopenharmony_ci if (n) 26958c2ecf20Sopenharmony_ci goto found; 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci } 26988c2ecf20Sopenharmony_ci return NULL; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_cifound: 27018c2ecf20Sopenharmony_ci iter->tb = tb; 27028c2ecf20Sopenharmony_ci return n; 27038c2ecf20Sopenharmony_ci} 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_cistatic void fib_trie_seq_stop(struct seq_file *seq, void *v) 27068c2ecf20Sopenharmony_ci __releases(RCU) 27078c2ecf20Sopenharmony_ci{ 27088c2ecf20Sopenharmony_ci rcu_read_unlock(); 27098c2ecf20Sopenharmony_ci} 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_cistatic void seq_indent(struct seq_file *seq, int n) 27128c2ecf20Sopenharmony_ci{ 27138c2ecf20Sopenharmony_ci while (n-- > 0) 27148c2ecf20Sopenharmony_ci seq_puts(seq, " "); 27158c2ecf20Sopenharmony_ci} 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_cistatic inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s) 27188c2ecf20Sopenharmony_ci{ 27198c2ecf20Sopenharmony_ci switch (s) { 27208c2ecf20Sopenharmony_ci case RT_SCOPE_UNIVERSE: return "universe"; 27218c2ecf20Sopenharmony_ci case RT_SCOPE_SITE: return "site"; 27228c2ecf20Sopenharmony_ci case RT_SCOPE_LINK: return "link"; 27238c2ecf20Sopenharmony_ci case RT_SCOPE_HOST: return "host"; 27248c2ecf20Sopenharmony_ci case RT_SCOPE_NOWHERE: return "nowhere"; 27258c2ecf20Sopenharmony_ci default: 27268c2ecf20Sopenharmony_ci snprintf(buf, len, "scope=%d", s); 27278c2ecf20Sopenharmony_ci return buf; 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci} 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_cistatic const char *const rtn_type_names[__RTN_MAX] = { 27328c2ecf20Sopenharmony_ci [RTN_UNSPEC] = "UNSPEC", 27338c2ecf20Sopenharmony_ci [RTN_UNICAST] = "UNICAST", 27348c2ecf20Sopenharmony_ci [RTN_LOCAL] = "LOCAL", 27358c2ecf20Sopenharmony_ci [RTN_BROADCAST] = "BROADCAST", 27368c2ecf20Sopenharmony_ci [RTN_ANYCAST] = "ANYCAST", 27378c2ecf20Sopenharmony_ci [RTN_MULTICAST] = "MULTICAST", 27388c2ecf20Sopenharmony_ci [RTN_BLACKHOLE] = "BLACKHOLE", 27398c2ecf20Sopenharmony_ci [RTN_UNREACHABLE] = "UNREACHABLE", 27408c2ecf20Sopenharmony_ci [RTN_PROHIBIT] = "PROHIBIT", 27418c2ecf20Sopenharmony_ci [RTN_THROW] = "THROW", 27428c2ecf20Sopenharmony_ci [RTN_NAT] = "NAT", 27438c2ecf20Sopenharmony_ci [RTN_XRESOLVE] = "XRESOLVE", 27448c2ecf20Sopenharmony_ci}; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_cistatic inline const char *rtn_type(char *buf, size_t len, unsigned int t) 27478c2ecf20Sopenharmony_ci{ 27488c2ecf20Sopenharmony_ci if (t < __RTN_MAX && rtn_type_names[t]) 27498c2ecf20Sopenharmony_ci return rtn_type_names[t]; 27508c2ecf20Sopenharmony_ci snprintf(buf, len, "type %u", t); 27518c2ecf20Sopenharmony_ci return buf; 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci/* Pretty print the trie */ 27558c2ecf20Sopenharmony_cistatic int fib_trie_seq_show(struct seq_file *seq, void *v) 27568c2ecf20Sopenharmony_ci{ 27578c2ecf20Sopenharmony_ci const struct fib_trie_iter *iter = seq->private; 27588c2ecf20Sopenharmony_ci struct key_vector *n = v; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci if (IS_TRIE(node_parent_rcu(n))) 27618c2ecf20Sopenharmony_ci fib_table_print(seq, iter->tb); 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci if (IS_TNODE(n)) { 27648c2ecf20Sopenharmony_ci __be32 prf = htonl(n->key); 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci seq_indent(seq, iter->depth-1); 27678c2ecf20Sopenharmony_ci seq_printf(seq, " +-- %pI4/%zu %u %u %u\n", 27688c2ecf20Sopenharmony_ci &prf, KEYLENGTH - n->pos - n->bits, n->bits, 27698c2ecf20Sopenharmony_ci tn_info(n)->full_children, 27708c2ecf20Sopenharmony_ci tn_info(n)->empty_children); 27718c2ecf20Sopenharmony_ci } else { 27728c2ecf20Sopenharmony_ci __be32 val = htonl(n->key); 27738c2ecf20Sopenharmony_ci struct fib_alias *fa; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci seq_indent(seq, iter->depth); 27768c2ecf20Sopenharmony_ci seq_printf(seq, " |-- %pI4\n", &val); 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) { 27798c2ecf20Sopenharmony_ci char buf1[32], buf2[32]; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci seq_indent(seq, iter->depth + 1); 27828c2ecf20Sopenharmony_ci seq_printf(seq, " /%zu %s %s", 27838c2ecf20Sopenharmony_ci KEYLENGTH - fa->fa_slen, 27848c2ecf20Sopenharmony_ci rtn_scope(buf1, sizeof(buf1), 27858c2ecf20Sopenharmony_ci fa->fa_info->fib_scope), 27868c2ecf20Sopenharmony_ci rtn_type(buf2, sizeof(buf2), 27878c2ecf20Sopenharmony_ci fa->fa_type)); 27888c2ecf20Sopenharmony_ci if (fa->fa_tos) 27898c2ecf20Sopenharmony_ci seq_printf(seq, " tos=%d", fa->fa_tos); 27908c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 27918c2ecf20Sopenharmony_ci } 27928c2ecf20Sopenharmony_ci } 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci return 0; 27958c2ecf20Sopenharmony_ci} 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_cistatic const struct seq_operations fib_trie_seq_ops = { 27988c2ecf20Sopenharmony_ci .start = fib_trie_seq_start, 27998c2ecf20Sopenharmony_ci .next = fib_trie_seq_next, 28008c2ecf20Sopenharmony_ci .stop = fib_trie_seq_stop, 28018c2ecf20Sopenharmony_ci .show = fib_trie_seq_show, 28028c2ecf20Sopenharmony_ci}; 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistruct fib_route_iter { 28058c2ecf20Sopenharmony_ci struct seq_net_private p; 28068c2ecf20Sopenharmony_ci struct fib_table *main_tb; 28078c2ecf20Sopenharmony_ci struct key_vector *tnode; 28088c2ecf20Sopenharmony_ci loff_t pos; 28098c2ecf20Sopenharmony_ci t_key key; 28108c2ecf20Sopenharmony_ci}; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_cistatic struct key_vector *fib_route_get_idx(struct fib_route_iter *iter, 28138c2ecf20Sopenharmony_ci loff_t pos) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci struct key_vector *l, **tp = &iter->tnode; 28168c2ecf20Sopenharmony_ci t_key key; 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci /* use cached location of previously found key */ 28198c2ecf20Sopenharmony_ci if (iter->pos > 0 && pos >= iter->pos) { 28208c2ecf20Sopenharmony_ci key = iter->key; 28218c2ecf20Sopenharmony_ci } else { 28228c2ecf20Sopenharmony_ci iter->pos = 1; 28238c2ecf20Sopenharmony_ci key = 0; 28248c2ecf20Sopenharmony_ci } 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci pos -= iter->pos; 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci while ((l = leaf_walk_rcu(tp, key)) && (pos-- > 0)) { 28298c2ecf20Sopenharmony_ci key = l->key + 1; 28308c2ecf20Sopenharmony_ci iter->pos++; 28318c2ecf20Sopenharmony_ci l = NULL; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci /* handle unlikely case of a key wrap */ 28348c2ecf20Sopenharmony_ci if (!key) 28358c2ecf20Sopenharmony_ci break; 28368c2ecf20Sopenharmony_ci } 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci if (l) 28398c2ecf20Sopenharmony_ci iter->key = l->key; /* remember it */ 28408c2ecf20Sopenharmony_ci else 28418c2ecf20Sopenharmony_ci iter->pos = 0; /* forget it */ 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci return l; 28448c2ecf20Sopenharmony_ci} 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_cistatic void *fib_route_seq_start(struct seq_file *seq, loff_t *pos) 28478c2ecf20Sopenharmony_ci __acquires(RCU) 28488c2ecf20Sopenharmony_ci{ 28498c2ecf20Sopenharmony_ci struct fib_route_iter *iter = seq->private; 28508c2ecf20Sopenharmony_ci struct fib_table *tb; 28518c2ecf20Sopenharmony_ci struct trie *t; 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci rcu_read_lock(); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci tb = fib_get_table(seq_file_net(seq), RT_TABLE_MAIN); 28568c2ecf20Sopenharmony_ci if (!tb) 28578c2ecf20Sopenharmony_ci return NULL; 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci iter->main_tb = tb; 28608c2ecf20Sopenharmony_ci t = (struct trie *)tb->tb_data; 28618c2ecf20Sopenharmony_ci iter->tnode = t->kv; 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci if (*pos != 0) 28648c2ecf20Sopenharmony_ci return fib_route_get_idx(iter, *pos); 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci iter->pos = 0; 28678c2ecf20Sopenharmony_ci iter->key = KEY_MAX; 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci return SEQ_START_TOKEN; 28708c2ecf20Sopenharmony_ci} 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_cistatic void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28738c2ecf20Sopenharmony_ci{ 28748c2ecf20Sopenharmony_ci struct fib_route_iter *iter = seq->private; 28758c2ecf20Sopenharmony_ci struct key_vector *l = NULL; 28768c2ecf20Sopenharmony_ci t_key key = iter->key + 1; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci ++*pos; 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci /* only allow key of 0 for start of sequence */ 28818c2ecf20Sopenharmony_ci if ((v == SEQ_START_TOKEN) || key) 28828c2ecf20Sopenharmony_ci l = leaf_walk_rcu(&iter->tnode, key); 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_ci if (l) { 28858c2ecf20Sopenharmony_ci iter->key = l->key; 28868c2ecf20Sopenharmony_ci iter->pos++; 28878c2ecf20Sopenharmony_ci } else { 28888c2ecf20Sopenharmony_ci iter->pos = 0; 28898c2ecf20Sopenharmony_ci } 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci return l; 28928c2ecf20Sopenharmony_ci} 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_cistatic void fib_route_seq_stop(struct seq_file *seq, void *v) 28958c2ecf20Sopenharmony_ci __releases(RCU) 28968c2ecf20Sopenharmony_ci{ 28978c2ecf20Sopenharmony_ci rcu_read_unlock(); 28988c2ecf20Sopenharmony_ci} 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_cistatic unsigned int fib_flag_trans(int type, __be32 mask, struct fib_info *fi) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci unsigned int flags = 0; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci if (type == RTN_UNREACHABLE || type == RTN_PROHIBIT) 29058c2ecf20Sopenharmony_ci flags = RTF_REJECT; 29068c2ecf20Sopenharmony_ci if (fi) { 29078c2ecf20Sopenharmony_ci const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci if (nhc->nhc_gw.ipv4) 29108c2ecf20Sopenharmony_ci flags |= RTF_GATEWAY; 29118c2ecf20Sopenharmony_ci } 29128c2ecf20Sopenharmony_ci if (mask == htonl(0xFFFFFFFF)) 29138c2ecf20Sopenharmony_ci flags |= RTF_HOST; 29148c2ecf20Sopenharmony_ci flags |= RTF_UP; 29158c2ecf20Sopenharmony_ci return flags; 29168c2ecf20Sopenharmony_ci} 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci/* 29198c2ecf20Sopenharmony_ci * This outputs /proc/net/route. 29208c2ecf20Sopenharmony_ci * The format of the file is not supposed to be changed 29218c2ecf20Sopenharmony_ci * and needs to be same as fib_hash output to avoid breaking 29228c2ecf20Sopenharmony_ci * legacy utilities 29238c2ecf20Sopenharmony_ci */ 29248c2ecf20Sopenharmony_cistatic int fib_route_seq_show(struct seq_file *seq, void *v) 29258c2ecf20Sopenharmony_ci{ 29268c2ecf20Sopenharmony_ci struct fib_route_iter *iter = seq->private; 29278c2ecf20Sopenharmony_ci struct fib_table *tb = iter->main_tb; 29288c2ecf20Sopenharmony_ci struct fib_alias *fa; 29298c2ecf20Sopenharmony_ci struct key_vector *l = v; 29308c2ecf20Sopenharmony_ci __be32 prefix; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 29338c2ecf20Sopenharmony_ci seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway " 29348c2ecf20Sopenharmony_ci "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" 29358c2ecf20Sopenharmony_ci "\tWindow\tIRTT"); 29368c2ecf20Sopenharmony_ci return 0; 29378c2ecf20Sopenharmony_ci } 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci prefix = htonl(l->key); 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 29428c2ecf20Sopenharmony_ci struct fib_info *fi = fa->fa_info; 29438c2ecf20Sopenharmony_ci __be32 mask = inet_make_mask(KEYLENGTH - fa->fa_slen); 29448c2ecf20Sopenharmony_ci unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci if ((fa->fa_type == RTN_BROADCAST) || 29478c2ecf20Sopenharmony_ci (fa->fa_type == RTN_MULTICAST)) 29488c2ecf20Sopenharmony_ci continue; 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci if (fa->tb_id != tb->tb_id) 29518c2ecf20Sopenharmony_ci continue; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci seq_setwidth(seq, 127); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci if (fi) { 29568c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = fib_info_nhc(fi, 0); 29578c2ecf20Sopenharmony_ci __be32 gw = 0; 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci if (nhc->nhc_gw_family == AF_INET) 29608c2ecf20Sopenharmony_ci gw = nhc->nhc_gw.ipv4; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci seq_printf(seq, 29638c2ecf20Sopenharmony_ci "%s\t%08X\t%08X\t%04X\t%d\t%u\t" 29648c2ecf20Sopenharmony_ci "%d\t%08X\t%d\t%u\t%u", 29658c2ecf20Sopenharmony_ci nhc->nhc_dev ? nhc->nhc_dev->name : "*", 29668c2ecf20Sopenharmony_ci prefix, gw, flags, 0, 0, 29678c2ecf20Sopenharmony_ci fi->fib_priority, 29688c2ecf20Sopenharmony_ci mask, 29698c2ecf20Sopenharmony_ci (fi->fib_advmss ? 29708c2ecf20Sopenharmony_ci fi->fib_advmss + 40 : 0), 29718c2ecf20Sopenharmony_ci fi->fib_window, 29728c2ecf20Sopenharmony_ci fi->fib_rtt >> 3); 29738c2ecf20Sopenharmony_ci } else { 29748c2ecf20Sopenharmony_ci seq_printf(seq, 29758c2ecf20Sopenharmony_ci "*\t%08X\t%08X\t%04X\t%d\t%u\t" 29768c2ecf20Sopenharmony_ci "%d\t%08X\t%d\t%u\t%u", 29778c2ecf20Sopenharmony_ci prefix, 0, flags, 0, 0, 0, 29788c2ecf20Sopenharmony_ci mask, 0, 0, 0); 29798c2ecf20Sopenharmony_ci } 29808c2ecf20Sopenharmony_ci seq_pad(seq, '\n'); 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci return 0; 29848c2ecf20Sopenharmony_ci} 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_cistatic const struct seq_operations fib_route_seq_ops = { 29878c2ecf20Sopenharmony_ci .start = fib_route_seq_start, 29888c2ecf20Sopenharmony_ci .next = fib_route_seq_next, 29898c2ecf20Sopenharmony_ci .stop = fib_route_seq_stop, 29908c2ecf20Sopenharmony_ci .show = fib_route_seq_show, 29918c2ecf20Sopenharmony_ci}; 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ciint __net_init fib_proc_init(struct net *net) 29948c2ecf20Sopenharmony_ci{ 29958c2ecf20Sopenharmony_ci if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, 29968c2ecf20Sopenharmony_ci sizeof(struct fib_trie_iter))) 29978c2ecf20Sopenharmony_ci goto out1; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci if (!proc_create_net_single("fib_triestat", 0444, net->proc_net, 30008c2ecf20Sopenharmony_ci fib_triestat_seq_show, NULL)) 30018c2ecf20Sopenharmony_ci goto out2; 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci if (!proc_create_net("route", 0444, net->proc_net, &fib_route_seq_ops, 30048c2ecf20Sopenharmony_ci sizeof(struct fib_route_iter))) 30058c2ecf20Sopenharmony_ci goto out3; 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci return 0; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ciout3: 30108c2ecf20Sopenharmony_ci remove_proc_entry("fib_triestat", net->proc_net); 30118c2ecf20Sopenharmony_ciout2: 30128c2ecf20Sopenharmony_ci remove_proc_entry("fib_trie", net->proc_net); 30138c2ecf20Sopenharmony_ciout1: 30148c2ecf20Sopenharmony_ci return -ENOMEM; 30158c2ecf20Sopenharmony_ci} 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_civoid __net_exit fib_proc_exit(struct net *net) 30188c2ecf20Sopenharmony_ci{ 30198c2ecf20Sopenharmony_ci remove_proc_entry("fib_trie", net->proc_net); 30208c2ecf20Sopenharmony_ci remove_proc_entry("fib_triestat", net->proc_net); 30218c2ecf20Sopenharmony_ci remove_proc_entry("route", net->proc_net); 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 3025