162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Robert Olsson <robert.olsson@its.uu.se> Uppsala Universitet 562306a36Sopenharmony_ci * & Swedish University of Agricultural Sciences. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Jens Laas <jens.laas@data.slu.se> Swedish University of 862306a36Sopenharmony_ci * Agricultural Sciences. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Hans Liss <hans.liss@its.uu.se> Uppsala Universitet 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This work is based on the LPC-trie which is originally described in: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * An experimental study of compression methods for dynamic tries 1562306a36Sopenharmony_ci * Stefan Nilsson and Matti Tikkanen. Algorithmica, 33(1):19-33, 2002. 1662306a36Sopenharmony_ci * https://www.csc.kth.se/~snilsson/software/dyntrie2/ 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson 1962306a36Sopenharmony_ci * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Code from fib_hash has been reused which includes the following header: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 2462306a36Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 2562306a36Sopenharmony_ci * interface as the means of communication with the user level. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * IPv4 FIB: lookup engine and maintenance routines. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Substantial contributions to this work comes from: 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * David S. Miller, <davem@davemloft.net> 3462306a36Sopenharmony_ci * Stephen Hemminger <shemminger@osdl.org> 3562306a36Sopenharmony_ci * Paul E. McKenney <paulmck@us.ibm.com> 3662306a36Sopenharmony_ci * Patrick McHardy <kaber@trash.net> 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#include <linux/cache.h> 3962306a36Sopenharmony_ci#include <linux/uaccess.h> 4062306a36Sopenharmony_ci#include <linux/bitops.h> 4162306a36Sopenharmony_ci#include <linux/types.h> 4262306a36Sopenharmony_ci#include <linux/kernel.h> 4362306a36Sopenharmony_ci#include <linux/mm.h> 4462306a36Sopenharmony_ci#include <linux/string.h> 4562306a36Sopenharmony_ci#include <linux/socket.h> 4662306a36Sopenharmony_ci#include <linux/sockios.h> 4762306a36Sopenharmony_ci#include <linux/errno.h> 4862306a36Sopenharmony_ci#include <linux/in.h> 4962306a36Sopenharmony_ci#include <linux/inet.h> 5062306a36Sopenharmony_ci#include <linux/inetdevice.h> 5162306a36Sopenharmony_ci#include <linux/netdevice.h> 5262306a36Sopenharmony_ci#include <linux/if_arp.h> 5362306a36Sopenharmony_ci#include <linux/proc_fs.h> 5462306a36Sopenharmony_ci#include <linux/rcupdate.h> 5562306a36Sopenharmony_ci#include <linux/skbuff.h> 5662306a36Sopenharmony_ci#include <linux/netlink.h> 5762306a36Sopenharmony_ci#include <linux/init.h> 5862306a36Sopenharmony_ci#include <linux/list.h> 5962306a36Sopenharmony_ci#include <linux/slab.h> 6062306a36Sopenharmony_ci#include <linux/export.h> 6162306a36Sopenharmony_ci#include <linux/vmalloc.h> 6262306a36Sopenharmony_ci#include <linux/notifier.h> 6362306a36Sopenharmony_ci#include <net/net_namespace.h> 6462306a36Sopenharmony_ci#include <net/inet_dscp.h> 6562306a36Sopenharmony_ci#include <net/ip.h> 6662306a36Sopenharmony_ci#include <net/protocol.h> 6762306a36Sopenharmony_ci#include <net/route.h> 6862306a36Sopenharmony_ci#include <net/tcp.h> 6962306a36Sopenharmony_ci#include <net/sock.h> 7062306a36Sopenharmony_ci#include <net/ip_fib.h> 7162306a36Sopenharmony_ci#include <net/fib_notifier.h> 7262306a36Sopenharmony_ci#include <trace/events/fib.h> 7362306a36Sopenharmony_ci#include "fib_lookup.h" 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int call_fib_entry_notifier(struct notifier_block *nb, 7662306a36Sopenharmony_ci enum fib_event_type event_type, u32 dst, 7762306a36Sopenharmony_ci int dst_len, struct fib_alias *fa, 7862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct fib_entry_notifier_info info = { 8162306a36Sopenharmony_ci .info.extack = extack, 8262306a36Sopenharmony_ci .dst = dst, 8362306a36Sopenharmony_ci .dst_len = dst_len, 8462306a36Sopenharmony_ci .fi = fa->fa_info, 8562306a36Sopenharmony_ci .dscp = fa->fa_dscp, 8662306a36Sopenharmony_ci .type = fa->fa_type, 8762306a36Sopenharmony_ci .tb_id = fa->tb_id, 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci return call_fib4_notifier(nb, event_type, &info.info); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int call_fib_entry_notifiers(struct net *net, 9362306a36Sopenharmony_ci enum fib_event_type event_type, u32 dst, 9462306a36Sopenharmony_ci int dst_len, struct fib_alias *fa, 9562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct fib_entry_notifier_info info = { 9862306a36Sopenharmony_ci .info.extack = extack, 9962306a36Sopenharmony_ci .dst = dst, 10062306a36Sopenharmony_ci .dst_len = dst_len, 10162306a36Sopenharmony_ci .fi = fa->fa_info, 10262306a36Sopenharmony_ci .dscp = fa->fa_dscp, 10362306a36Sopenharmony_ci .type = fa->fa_type, 10462306a36Sopenharmony_ci .tb_id = fa->tb_id, 10562306a36Sopenharmony_ci }; 10662306a36Sopenharmony_ci return call_fib4_notifiers(net, event_type, &info.info); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define MAX_STAT_DEPTH 32 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define KEYLENGTH (8*sizeof(t_key)) 11262306a36Sopenharmony_ci#define KEY_MAX ((t_key)~0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_citypedef unsigned int t_key; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define IS_TRIE(n) ((n)->pos >= KEYLENGTH) 11762306a36Sopenharmony_ci#define IS_TNODE(n) ((n)->bits) 11862306a36Sopenharmony_ci#define IS_LEAF(n) (!(n)->bits) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct key_vector { 12162306a36Sopenharmony_ci t_key key; 12262306a36Sopenharmony_ci unsigned char pos; /* 2log(KEYLENGTH) bits needed */ 12362306a36Sopenharmony_ci unsigned char bits; /* 2log(KEYLENGTH) bits needed */ 12462306a36Sopenharmony_ci unsigned char slen; 12562306a36Sopenharmony_ci union { 12662306a36Sopenharmony_ci /* This list pointer if valid if (pos | bits) == 0 (LEAF) */ 12762306a36Sopenharmony_ci struct hlist_head leaf; 12862306a36Sopenharmony_ci /* This array is valid if (pos | bits) > 0 (TNODE) */ 12962306a36Sopenharmony_ci DECLARE_FLEX_ARRAY(struct key_vector __rcu *, tnode); 13062306a36Sopenharmony_ci }; 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct tnode { 13462306a36Sopenharmony_ci struct rcu_head rcu; 13562306a36Sopenharmony_ci t_key empty_children; /* KEYLENGTH bits needed */ 13662306a36Sopenharmony_ci t_key full_children; /* KEYLENGTH bits needed */ 13762306a36Sopenharmony_ci struct key_vector __rcu *parent; 13862306a36Sopenharmony_ci struct key_vector kv[1]; 13962306a36Sopenharmony_ci#define tn_bits kv[0].bits 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define TNODE_SIZE(n) offsetof(struct tnode, kv[0].tnode[n]) 14362306a36Sopenharmony_ci#define LEAF_SIZE TNODE_SIZE(1) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 14662306a36Sopenharmony_cistruct trie_use_stats { 14762306a36Sopenharmony_ci unsigned int gets; 14862306a36Sopenharmony_ci unsigned int backtrack; 14962306a36Sopenharmony_ci unsigned int semantic_match_passed; 15062306a36Sopenharmony_ci unsigned int semantic_match_miss; 15162306a36Sopenharmony_ci unsigned int null_node_hit; 15262306a36Sopenharmony_ci unsigned int resize_node_skipped; 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistruct trie_stat { 15762306a36Sopenharmony_ci unsigned int totdepth; 15862306a36Sopenharmony_ci unsigned int maxdepth; 15962306a36Sopenharmony_ci unsigned int tnodes; 16062306a36Sopenharmony_ci unsigned int leaves; 16162306a36Sopenharmony_ci unsigned int nullpointers; 16262306a36Sopenharmony_ci unsigned int prefixes; 16362306a36Sopenharmony_ci unsigned int nodesizes[MAX_STAT_DEPTH]; 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistruct trie { 16762306a36Sopenharmony_ci struct key_vector kv[1]; 16862306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 16962306a36Sopenharmony_ci struct trie_use_stats __percpu *stats; 17062306a36Sopenharmony_ci#endif 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn); 17462306a36Sopenharmony_cistatic unsigned int tnode_free_size; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * synchronize_rcu after call_rcu for outstanding dirty memory; it should be 17862306a36Sopenharmony_ci * especially useful before resizing the root node with PREEMPT_NONE configs; 17962306a36Sopenharmony_ci * the value was obtained experimentally, aiming to avoid visible slowdown. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ciunsigned int sysctl_fib_sync_mem = 512 * 1024; 18262306a36Sopenharmony_ciunsigned int sysctl_fib_sync_mem_min = 64 * 1024; 18362306a36Sopenharmony_ciunsigned int sysctl_fib_sync_mem_max = 64 * 1024 * 1024; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct kmem_cache *fn_alias_kmem __ro_after_init; 18662306a36Sopenharmony_cistatic struct kmem_cache *trie_leaf_kmem __ro_after_init; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic inline struct tnode *tn_info(struct key_vector *kv) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return container_of(kv, struct tnode, kv[0]); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* caller must hold RTNL */ 19462306a36Sopenharmony_ci#define node_parent(tn) rtnl_dereference(tn_info(tn)->parent) 19562306a36Sopenharmony_ci#define get_child(tn, i) rtnl_dereference((tn)->tnode[i]) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* caller must hold RCU read lock or RTNL */ 19862306a36Sopenharmony_ci#define node_parent_rcu(tn) rcu_dereference_rtnl(tn_info(tn)->parent) 19962306a36Sopenharmony_ci#define get_child_rcu(tn, i) rcu_dereference_rtnl((tn)->tnode[i]) 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* wrapper for rcu_assign_pointer */ 20262306a36Sopenharmony_cistatic inline void node_set_parent(struct key_vector *n, struct key_vector *tp) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci if (n) 20562306a36Sopenharmony_ci rcu_assign_pointer(tn_info(n)->parent, tp); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER(tn_info(n)->parent, p) 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* This provides us with the number of children in this node, in the case of a 21162306a36Sopenharmony_ci * leaf this will return 0 meaning none of the children are accessible. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic inline unsigned long child_length(const struct key_vector *tn) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci return (1ul << tn->bits) & ~(1ul); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci#define get_cindex(key, kv) (((key) ^ (kv)->key) >> (kv)->pos) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic inline unsigned long get_index(t_key key, struct key_vector *kv) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unsigned long index = key ^ kv->key; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if ((BITS_PER_LONG <= KEYLENGTH) && (KEYLENGTH == kv->pos)) 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return index >> kv->pos; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* To understand this stuff, an understanding of keys and all their bits is 23162306a36Sopenharmony_ci * necessary. Every node in the trie has a key associated with it, but not 23262306a36Sopenharmony_ci * all of the bits in that key are significant. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Consider a node 'n' and its parent 'tp'. 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * If n is a leaf, every bit in its key is significant. Its presence is 23762306a36Sopenharmony_ci * necessitated by path compression, since during a tree traversal (when 23862306a36Sopenharmony_ci * searching for a leaf - unless we are doing an insertion) we will completely 23962306a36Sopenharmony_ci * ignore all skipped bits we encounter. Thus we need to verify, at the end of 24062306a36Sopenharmony_ci * a potentially successful search, that we have indeed been walking the 24162306a36Sopenharmony_ci * correct key path. 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Note that we can never "miss" the correct key in the tree if present by 24462306a36Sopenharmony_ci * following the wrong path. Path compression ensures that segments of the key 24562306a36Sopenharmony_ci * that are the same for all keys with a given prefix are skipped, but the 24662306a36Sopenharmony_ci * skipped part *is* identical for each node in the subtrie below the skipped 24762306a36Sopenharmony_ci * bit! trie_insert() in this implementation takes care of that. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * if n is an internal node - a 'tnode' here, the various parts of its key 25062306a36Sopenharmony_ci * have many different meanings. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * Example: 25362306a36Sopenharmony_ci * _________________________________________________________________ 25462306a36Sopenharmony_ci * | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C | 25562306a36Sopenharmony_ci * ----------------------------------------------------------------- 25662306a36Sopenharmony_ci * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * _________________________________________________________________ 25962306a36Sopenharmony_ci * | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u | 26062306a36Sopenharmony_ci * ----------------------------------------------------------------- 26162306a36Sopenharmony_ci * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * tp->pos = 22 26462306a36Sopenharmony_ci * tp->bits = 3 26562306a36Sopenharmony_ci * n->pos = 13 26662306a36Sopenharmony_ci * n->bits = 4 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * First, let's just ignore the bits that come before the parent tp, that is 26962306a36Sopenharmony_ci * the bits from (tp->pos + tp->bits) to 31. They are *known* but at this 27062306a36Sopenharmony_ci * point we do not use them for anything. 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the 27362306a36Sopenharmony_ci * index into the parent's child array. That is, they will be used to find 27462306a36Sopenharmony_ci * 'n' among tp's children. 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * The bits from (n->pos + n->bits) to (tp->pos - 1) - "S" - are skipped bits 27762306a36Sopenharmony_ci * for the node n. 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * All the bits we have seen so far are significant to the node n. The rest 28062306a36Sopenharmony_ci * of the bits are really not needed or indeed known in n->key. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into 28362306a36Sopenharmony_ci * n's child array, and will of course be different for each child. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * The rest of the bits, from 0 to (n->pos -1) - "u" - are completely unknown 28662306a36Sopenharmony_ci * at this point. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const int halve_threshold = 25; 29062306a36Sopenharmony_cistatic const int inflate_threshold = 50; 29162306a36Sopenharmony_cistatic const int halve_threshold_root = 15; 29262306a36Sopenharmony_cistatic const int inflate_threshold_root = 30; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void __alias_free_mem(struct rcu_head *head) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct fib_alias *fa = container_of(head, struct fib_alias, rcu); 29762306a36Sopenharmony_ci kmem_cache_free(fn_alias_kmem, fa); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline void alias_free_mem_rcu(struct fib_alias *fa) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci call_rcu(&fa->rcu, __alias_free_mem); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci#define TNODE_VMALLOC_MAX \ 30662306a36Sopenharmony_ci ilog2((SIZE_MAX - TNODE_SIZE(0)) / sizeof(struct key_vector *)) 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void __node_free_rcu(struct rcu_head *head) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct tnode *n = container_of(head, struct tnode, rcu); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (!n->tn_bits) 31362306a36Sopenharmony_ci kmem_cache_free(trie_leaf_kmem, n); 31462306a36Sopenharmony_ci else 31562306a36Sopenharmony_ci kvfree(n); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci#define node_free(n) call_rcu(&tn_info(n)->rcu, __node_free_rcu) 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic struct tnode *tnode_alloc(int bits) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci size_t size; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* verify bits is within bounds */ 32562306a36Sopenharmony_ci if (bits > TNODE_VMALLOC_MAX) 32662306a36Sopenharmony_ci return NULL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* determine size and verify it is non-zero and didn't overflow */ 32962306a36Sopenharmony_ci size = TNODE_SIZE(1ul << bits); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (size <= PAGE_SIZE) 33262306a36Sopenharmony_ci return kzalloc(size, GFP_KERNEL); 33362306a36Sopenharmony_ci else 33462306a36Sopenharmony_ci return vzalloc(size); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic inline void empty_child_inc(struct key_vector *n) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci tn_info(n)->empty_children++; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!tn_info(n)->empty_children) 34262306a36Sopenharmony_ci tn_info(n)->full_children++; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic inline void empty_child_dec(struct key_vector *n) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci if (!tn_info(n)->empty_children) 34862306a36Sopenharmony_ci tn_info(n)->full_children--; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci tn_info(n)->empty_children--; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic struct key_vector *leaf_new(t_key key, struct fib_alias *fa) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct key_vector *l; 35662306a36Sopenharmony_ci struct tnode *kv; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci kv = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL); 35962306a36Sopenharmony_ci if (!kv) 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* initialize key vector */ 36362306a36Sopenharmony_ci l = kv->kv; 36462306a36Sopenharmony_ci l->key = key; 36562306a36Sopenharmony_ci l->pos = 0; 36662306a36Sopenharmony_ci l->bits = 0; 36762306a36Sopenharmony_ci l->slen = fa->fa_slen; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* link leaf to fib alias */ 37062306a36Sopenharmony_ci INIT_HLIST_HEAD(&l->leaf); 37162306a36Sopenharmony_ci hlist_add_head(&fa->fa_list, &l->leaf); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return l; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic struct key_vector *tnode_new(t_key key, int pos, int bits) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci unsigned int shift = pos + bits; 37962306a36Sopenharmony_ci struct key_vector *tn; 38062306a36Sopenharmony_ci struct tnode *tnode; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* verify bits and pos their msb bits clear and values are valid */ 38362306a36Sopenharmony_ci BUG_ON(!bits || (shift > KEYLENGTH)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci tnode = tnode_alloc(bits); 38662306a36Sopenharmony_ci if (!tnode) 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci pr_debug("AT %p s=%zu %zu\n", tnode, TNODE_SIZE(0), 39062306a36Sopenharmony_ci sizeof(struct key_vector *) << bits); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (bits == KEYLENGTH) 39362306a36Sopenharmony_ci tnode->full_children = 1; 39462306a36Sopenharmony_ci else 39562306a36Sopenharmony_ci tnode->empty_children = 1ul << bits; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tn = tnode->kv; 39862306a36Sopenharmony_ci tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0; 39962306a36Sopenharmony_ci tn->pos = pos; 40062306a36Sopenharmony_ci tn->bits = bits; 40162306a36Sopenharmony_ci tn->slen = pos; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return tn; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* Check whether a tnode 'n' is "full", i.e. it is an internal node 40762306a36Sopenharmony_ci * and no bits are skipped. See discussion in dyntree paper p. 6 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistatic inline int tnode_full(struct key_vector *tn, struct key_vector *n) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Add a child at position i overwriting the old value. 41562306a36Sopenharmony_ci * Update the value of full_children and empty_children. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_cistatic void put_child(struct key_vector *tn, unsigned long i, 41862306a36Sopenharmony_ci struct key_vector *n) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct key_vector *chi = get_child(tn, i); 42162306a36Sopenharmony_ci int isfull, wasfull; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci BUG_ON(i >= child_length(tn)); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* update emptyChildren, overflow into fullChildren */ 42662306a36Sopenharmony_ci if (!n && chi) 42762306a36Sopenharmony_ci empty_child_inc(tn); 42862306a36Sopenharmony_ci if (n && !chi) 42962306a36Sopenharmony_ci empty_child_dec(tn); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* update fullChildren */ 43262306a36Sopenharmony_ci wasfull = tnode_full(tn, chi); 43362306a36Sopenharmony_ci isfull = tnode_full(tn, n); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (wasfull && !isfull) 43662306a36Sopenharmony_ci tn_info(tn)->full_children--; 43762306a36Sopenharmony_ci else if (!wasfull && isfull) 43862306a36Sopenharmony_ci tn_info(tn)->full_children++; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (n && (tn->slen < n->slen)) 44162306a36Sopenharmony_ci tn->slen = n->slen; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci rcu_assign_pointer(tn->tnode[i], n); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void update_children(struct key_vector *tn) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci unsigned long i; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* update all of the child parent pointers */ 45162306a36Sopenharmony_ci for (i = child_length(tn); i;) { 45262306a36Sopenharmony_ci struct key_vector *inode = get_child(tn, --i); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (!inode) 45562306a36Sopenharmony_ci continue; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Either update the children of a tnode that 45862306a36Sopenharmony_ci * already belongs to us or update the child 45962306a36Sopenharmony_ci * to point to ourselves. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (node_parent(inode) == tn) 46262306a36Sopenharmony_ci update_children(inode); 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci node_set_parent(inode, tn); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic inline void put_child_root(struct key_vector *tp, t_key key, 46962306a36Sopenharmony_ci struct key_vector *n) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci if (IS_TRIE(tp)) 47262306a36Sopenharmony_ci rcu_assign_pointer(tp->tnode[0], n); 47362306a36Sopenharmony_ci else 47462306a36Sopenharmony_ci put_child(tp, get_index(key, tp), n); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic inline void tnode_free_init(struct key_vector *tn) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci tn_info(tn)->rcu.next = NULL; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic inline void tnode_free_append(struct key_vector *tn, 48362306a36Sopenharmony_ci struct key_vector *n) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci tn_info(n)->rcu.next = tn_info(tn)->rcu.next; 48662306a36Sopenharmony_ci tn_info(tn)->rcu.next = &tn_info(n)->rcu; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void tnode_free(struct key_vector *tn) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct callback_head *head = &tn_info(tn)->rcu; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci while (head) { 49462306a36Sopenharmony_ci head = head->next; 49562306a36Sopenharmony_ci tnode_free_size += TNODE_SIZE(1ul << tn->bits); 49662306a36Sopenharmony_ci node_free(tn); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci tn = container_of(head, struct tnode, rcu)->kv; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (tnode_free_size >= READ_ONCE(sysctl_fib_sync_mem)) { 50262306a36Sopenharmony_ci tnode_free_size = 0; 50362306a36Sopenharmony_ci synchronize_rcu(); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic struct key_vector *replace(struct trie *t, 50862306a36Sopenharmony_ci struct key_vector *oldtnode, 50962306a36Sopenharmony_ci struct key_vector *tn) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct key_vector *tp = node_parent(oldtnode); 51262306a36Sopenharmony_ci unsigned long i; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* setup the parent pointer out of and back into this node */ 51562306a36Sopenharmony_ci NODE_INIT_PARENT(tn, tp); 51662306a36Sopenharmony_ci put_child_root(tp, tn->key, tn); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* update all of the child parent pointers */ 51962306a36Sopenharmony_ci update_children(tn); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* all pointers should be clean so we are done */ 52262306a36Sopenharmony_ci tnode_free(oldtnode); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* resize children now that oldtnode is freed */ 52562306a36Sopenharmony_ci for (i = child_length(tn); i;) { 52662306a36Sopenharmony_ci struct key_vector *inode = get_child(tn, --i); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* resize child node */ 52962306a36Sopenharmony_ci if (tnode_full(tn, inode)) 53062306a36Sopenharmony_ci tn = resize(t, inode); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return tp; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic struct key_vector *inflate(struct trie *t, 53762306a36Sopenharmony_ci struct key_vector *oldtnode) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct key_vector *tn; 54062306a36Sopenharmony_ci unsigned long i; 54162306a36Sopenharmony_ci t_key m; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci pr_debug("In inflate\n"); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1); 54662306a36Sopenharmony_ci if (!tn) 54762306a36Sopenharmony_ci goto notnode; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* prepare oldtnode to be freed */ 55062306a36Sopenharmony_ci tnode_free_init(oldtnode); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Assemble all of the pointers in our cluster, in this case that 55362306a36Sopenharmony_ci * represents all of the pointers out of our allocated nodes that 55462306a36Sopenharmony_ci * point to existing tnodes and the links between our allocated 55562306a36Sopenharmony_ci * nodes. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci for (i = child_length(oldtnode), m = 1u << tn->pos; i;) { 55862306a36Sopenharmony_ci struct key_vector *inode = get_child(oldtnode, --i); 55962306a36Sopenharmony_ci struct key_vector *node0, *node1; 56062306a36Sopenharmony_ci unsigned long j, k; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* An empty child */ 56362306a36Sopenharmony_ci if (!inode) 56462306a36Sopenharmony_ci continue; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* A leaf or an internal node with skipped bits */ 56762306a36Sopenharmony_ci if (!tnode_full(oldtnode, inode)) { 56862306a36Sopenharmony_ci put_child(tn, get_index(inode->key, tn), inode); 56962306a36Sopenharmony_ci continue; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* drop the node in the old tnode free list */ 57362306a36Sopenharmony_ci tnode_free_append(oldtnode, inode); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* An internal node with two children */ 57662306a36Sopenharmony_ci if (inode->bits == 1) { 57762306a36Sopenharmony_ci put_child(tn, 2 * i + 1, get_child(inode, 1)); 57862306a36Sopenharmony_ci put_child(tn, 2 * i, get_child(inode, 0)); 57962306a36Sopenharmony_ci continue; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* We will replace this node 'inode' with two new 58362306a36Sopenharmony_ci * ones, 'node0' and 'node1', each with half of the 58462306a36Sopenharmony_ci * original children. The two new nodes will have 58562306a36Sopenharmony_ci * a position one bit further down the key and this 58662306a36Sopenharmony_ci * means that the "significant" part of their keys 58762306a36Sopenharmony_ci * (see the discussion near the top of this file) 58862306a36Sopenharmony_ci * will differ by one bit, which will be "0" in 58962306a36Sopenharmony_ci * node0's key and "1" in node1's key. Since we are 59062306a36Sopenharmony_ci * moving the key position by one step, the bit that 59162306a36Sopenharmony_ci * we are moving away from - the bit at position 59262306a36Sopenharmony_ci * (tn->pos) - is the one that will differ between 59362306a36Sopenharmony_ci * node0 and node1. So... we synthesize that bit in the 59462306a36Sopenharmony_ci * two new keys. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1); 59762306a36Sopenharmony_ci if (!node1) 59862306a36Sopenharmony_ci goto nomem; 59962306a36Sopenharmony_ci node0 = tnode_new(inode->key, inode->pos, inode->bits - 1); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci tnode_free_append(tn, node1); 60262306a36Sopenharmony_ci if (!node0) 60362306a36Sopenharmony_ci goto nomem; 60462306a36Sopenharmony_ci tnode_free_append(tn, node0); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* populate child pointers in new nodes */ 60762306a36Sopenharmony_ci for (k = child_length(inode), j = k / 2; j;) { 60862306a36Sopenharmony_ci put_child(node1, --j, get_child(inode, --k)); 60962306a36Sopenharmony_ci put_child(node0, j, get_child(inode, j)); 61062306a36Sopenharmony_ci put_child(node1, --j, get_child(inode, --k)); 61162306a36Sopenharmony_ci put_child(node0, j, get_child(inode, j)); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* link new nodes to parent */ 61562306a36Sopenharmony_ci NODE_INIT_PARENT(node1, tn); 61662306a36Sopenharmony_ci NODE_INIT_PARENT(node0, tn); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* link parent to nodes */ 61962306a36Sopenharmony_ci put_child(tn, 2 * i + 1, node1); 62062306a36Sopenharmony_ci put_child(tn, 2 * i, node0); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* setup the parent pointers into and out of this node */ 62462306a36Sopenharmony_ci return replace(t, oldtnode, tn); 62562306a36Sopenharmony_cinomem: 62662306a36Sopenharmony_ci /* all pointers should be clean so we are done */ 62762306a36Sopenharmony_ci tnode_free(tn); 62862306a36Sopenharmony_cinotnode: 62962306a36Sopenharmony_ci return NULL; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic struct key_vector *halve(struct trie *t, 63362306a36Sopenharmony_ci struct key_vector *oldtnode) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct key_vector *tn; 63662306a36Sopenharmony_ci unsigned long i; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci pr_debug("In halve\n"); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1); 64162306a36Sopenharmony_ci if (!tn) 64262306a36Sopenharmony_ci goto notnode; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* prepare oldtnode to be freed */ 64562306a36Sopenharmony_ci tnode_free_init(oldtnode); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* Assemble all of the pointers in our cluster, in this case that 64862306a36Sopenharmony_ci * represents all of the pointers out of our allocated nodes that 64962306a36Sopenharmony_ci * point to existing tnodes and the links between our allocated 65062306a36Sopenharmony_ci * nodes. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci for (i = child_length(oldtnode); i;) { 65362306a36Sopenharmony_ci struct key_vector *node1 = get_child(oldtnode, --i); 65462306a36Sopenharmony_ci struct key_vector *node0 = get_child(oldtnode, --i); 65562306a36Sopenharmony_ci struct key_vector *inode; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* At least one of the children is empty */ 65862306a36Sopenharmony_ci if (!node1 || !node0) { 65962306a36Sopenharmony_ci put_child(tn, i / 2, node1 ? : node0); 66062306a36Sopenharmony_ci continue; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Two nonempty children */ 66462306a36Sopenharmony_ci inode = tnode_new(node0->key, oldtnode->pos, 1); 66562306a36Sopenharmony_ci if (!inode) 66662306a36Sopenharmony_ci goto nomem; 66762306a36Sopenharmony_ci tnode_free_append(tn, inode); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* initialize pointers out of node */ 67062306a36Sopenharmony_ci put_child(inode, 1, node1); 67162306a36Sopenharmony_ci put_child(inode, 0, node0); 67262306a36Sopenharmony_ci NODE_INIT_PARENT(inode, tn); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* link parent to node */ 67562306a36Sopenharmony_ci put_child(tn, i / 2, inode); 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* setup the parent pointers into and out of this node */ 67962306a36Sopenharmony_ci return replace(t, oldtnode, tn); 68062306a36Sopenharmony_cinomem: 68162306a36Sopenharmony_ci /* all pointers should be clean so we are done */ 68262306a36Sopenharmony_ci tnode_free(tn); 68362306a36Sopenharmony_cinotnode: 68462306a36Sopenharmony_ci return NULL; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic struct key_vector *collapse(struct trie *t, 68862306a36Sopenharmony_ci struct key_vector *oldtnode) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct key_vector *n, *tp; 69162306a36Sopenharmony_ci unsigned long i; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* scan the tnode looking for that one child that might still exist */ 69462306a36Sopenharmony_ci for (n = NULL, i = child_length(oldtnode); !n && i;) 69562306a36Sopenharmony_ci n = get_child(oldtnode, --i); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* compress one level */ 69862306a36Sopenharmony_ci tp = node_parent(oldtnode); 69962306a36Sopenharmony_ci put_child_root(tp, oldtnode->key, n); 70062306a36Sopenharmony_ci node_set_parent(n, tp); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* drop dead node */ 70362306a36Sopenharmony_ci node_free(oldtnode); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return tp; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic unsigned char update_suffix(struct key_vector *tn) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci unsigned char slen = tn->pos; 71162306a36Sopenharmony_ci unsigned long stride, i; 71262306a36Sopenharmony_ci unsigned char slen_max; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* only vector 0 can have a suffix length greater than or equal to 71562306a36Sopenharmony_ci * tn->pos + tn->bits, the second highest node will have a suffix 71662306a36Sopenharmony_ci * length at most of tn->pos + tn->bits - 1 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci slen_max = min_t(unsigned char, tn->pos + tn->bits - 1, tn->slen); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* search though the list of children looking for nodes that might 72162306a36Sopenharmony_ci * have a suffix greater than the one we currently have. This is 72262306a36Sopenharmony_ci * why we start with a stride of 2 since a stride of 1 would 72362306a36Sopenharmony_ci * represent the nodes with suffix length equal to tn->pos 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci for (i = 0, stride = 0x2ul ; i < child_length(tn); i += stride) { 72662306a36Sopenharmony_ci struct key_vector *n = get_child(tn, i); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (!n || (n->slen <= slen)) 72962306a36Sopenharmony_ci continue; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* update stride and slen based on new value */ 73262306a36Sopenharmony_ci stride <<= (n->slen - slen); 73362306a36Sopenharmony_ci slen = n->slen; 73462306a36Sopenharmony_ci i &= ~(stride - 1); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* stop searching if we have hit the maximum possible value */ 73762306a36Sopenharmony_ci if (slen >= slen_max) 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci tn->slen = slen; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return slen; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of 74762306a36Sopenharmony_ci * the Helsinki University of Technology and Matti Tikkanen of Nokia 74862306a36Sopenharmony_ci * Telecommunications, page 6: 74962306a36Sopenharmony_ci * "A node is doubled if the ratio of non-empty children to all 75062306a36Sopenharmony_ci * children in the *doubled* node is at least 'high'." 75162306a36Sopenharmony_ci * 75262306a36Sopenharmony_ci * 'high' in this instance is the variable 'inflate_threshold'. It 75362306a36Sopenharmony_ci * is expressed as a percentage, so we multiply it with 75462306a36Sopenharmony_ci * child_length() and instead of multiplying by 2 (since the 75562306a36Sopenharmony_ci * child array will be doubled by inflate()) and multiplying 75662306a36Sopenharmony_ci * the left-hand side by 100 (to handle the percentage thing) we 75762306a36Sopenharmony_ci * multiply the left-hand side by 50. 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * The left-hand side may look a bit weird: child_length(tn) 76062306a36Sopenharmony_ci * - tn->empty_children is of course the number of non-null children 76162306a36Sopenharmony_ci * in the current node. tn->full_children is the number of "full" 76262306a36Sopenharmony_ci * children, that is non-null tnodes with a skip value of 0. 76362306a36Sopenharmony_ci * All of those will be doubled in the resulting inflated tnode, so 76462306a36Sopenharmony_ci * we just count them one extra time here. 76562306a36Sopenharmony_ci * 76662306a36Sopenharmony_ci * A clearer way to write this would be: 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * to_be_doubled = tn->full_children; 76962306a36Sopenharmony_ci * not_to_be_doubled = child_length(tn) - tn->empty_children - 77062306a36Sopenharmony_ci * tn->full_children; 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * new_child_length = child_length(tn) * 2; 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) / 77562306a36Sopenharmony_ci * new_child_length; 77662306a36Sopenharmony_ci * if (new_fill_factor >= inflate_threshold) 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * ...and so on, tho it would mess up the while () loop. 77962306a36Sopenharmony_ci * 78062306a36Sopenharmony_ci * anyway, 78162306a36Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >= 78262306a36Sopenharmony_ci * inflate_threshold 78362306a36Sopenharmony_ci * 78462306a36Sopenharmony_ci * avoid a division: 78562306a36Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) >= 78662306a36Sopenharmony_ci * inflate_threshold * new_child_length 78762306a36Sopenharmony_ci * 78862306a36Sopenharmony_ci * expand not_to_be_doubled and to_be_doubled, and shorten: 78962306a36Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children + 79062306a36Sopenharmony_ci * tn->full_children) >= inflate_threshold * new_child_length 79162306a36Sopenharmony_ci * 79262306a36Sopenharmony_ci * expand new_child_length: 79362306a36Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children + 79462306a36Sopenharmony_ci * tn->full_children) >= 79562306a36Sopenharmony_ci * inflate_threshold * child_length(tn) * 2 79662306a36Sopenharmony_ci * 79762306a36Sopenharmony_ci * shorten again: 79862306a36Sopenharmony_ci * 50 * (tn->full_children + child_length(tn) - 79962306a36Sopenharmony_ci * tn->empty_children) >= inflate_threshold * 80062306a36Sopenharmony_ci * child_length(tn) 80162306a36Sopenharmony_ci * 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_cistatic inline bool should_inflate(struct key_vector *tp, struct key_vector *tn) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci unsigned long used = child_length(tn); 80662306a36Sopenharmony_ci unsigned long threshold = used; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Keep root node larger */ 80962306a36Sopenharmony_ci threshold *= IS_TRIE(tp) ? inflate_threshold_root : inflate_threshold; 81062306a36Sopenharmony_ci used -= tn_info(tn)->empty_children; 81162306a36Sopenharmony_ci used += tn_info(tn)->full_children; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* if bits == KEYLENGTH then pos = 0, and will fail below */ 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return (used > 1) && tn->pos && ((50 * used) >= threshold); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic inline bool should_halve(struct key_vector *tp, struct key_vector *tn) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci unsigned long used = child_length(tn); 82162306a36Sopenharmony_ci unsigned long threshold = used; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* Keep root node larger */ 82462306a36Sopenharmony_ci threshold *= IS_TRIE(tp) ? halve_threshold_root : halve_threshold; 82562306a36Sopenharmony_ci used -= tn_info(tn)->empty_children; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* if bits == KEYLENGTH then used = 100% on wrap, and will fail below */ 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return (used > 1) && (tn->bits > 1) && ((100 * used) < threshold); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic inline bool should_collapse(struct key_vector *tn) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci unsigned long used = child_length(tn); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci used -= tn_info(tn)->empty_children; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* account for bits == KEYLENGTH case */ 83962306a36Sopenharmony_ci if ((tn->bits == KEYLENGTH) && tn_info(tn)->full_children) 84062306a36Sopenharmony_ci used -= KEY_MAX; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* One child or none, time to drop us from the trie */ 84362306a36Sopenharmony_ci return used < 2; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci#define MAX_WORK 10 84762306a36Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 85062306a36Sopenharmony_ci struct trie_use_stats __percpu *stats = t->stats; 85162306a36Sopenharmony_ci#endif 85262306a36Sopenharmony_ci struct key_vector *tp = node_parent(tn); 85362306a36Sopenharmony_ci unsigned long cindex = get_index(tn->key, tp); 85462306a36Sopenharmony_ci int max_work = MAX_WORK; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n", 85762306a36Sopenharmony_ci tn, inflate_threshold, halve_threshold); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* track the tnode via the pointer from the parent instead of 86062306a36Sopenharmony_ci * doing it ourselves. This way we can let RCU fully do its 86162306a36Sopenharmony_ci * thing without us interfering 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci BUG_ON(tn != get_child(tp, cindex)); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Double as long as the resulting node has a number of 86662306a36Sopenharmony_ci * nonempty nodes that are above the threshold. 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_ci while (should_inflate(tp, tn) && max_work) { 86962306a36Sopenharmony_ci tp = inflate(t, tn); 87062306a36Sopenharmony_ci if (!tp) { 87162306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 87262306a36Sopenharmony_ci this_cpu_inc(stats->resize_node_skipped); 87362306a36Sopenharmony_ci#endif 87462306a36Sopenharmony_ci break; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci max_work--; 87862306a36Sopenharmony_ci tn = get_child(tp, cindex); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* update parent in case inflate failed */ 88262306a36Sopenharmony_ci tp = node_parent(tn); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Return if at least one inflate is run */ 88562306a36Sopenharmony_ci if (max_work != MAX_WORK) 88662306a36Sopenharmony_ci return tp; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Halve as long as the number of empty children in this 88962306a36Sopenharmony_ci * node is above threshold. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci while (should_halve(tp, tn) && max_work) { 89262306a36Sopenharmony_ci tp = halve(t, tn); 89362306a36Sopenharmony_ci if (!tp) { 89462306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 89562306a36Sopenharmony_ci this_cpu_inc(stats->resize_node_skipped); 89662306a36Sopenharmony_ci#endif 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci max_work--; 90162306a36Sopenharmony_ci tn = get_child(tp, cindex); 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Only one child remains */ 90562306a36Sopenharmony_ci if (should_collapse(tn)) 90662306a36Sopenharmony_ci return collapse(t, tn); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* update parent in case halve failed */ 90962306a36Sopenharmony_ci return node_parent(tn); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void node_pull_suffix(struct key_vector *tn, unsigned char slen) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci unsigned char node_slen = tn->slen; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci while ((node_slen > tn->pos) && (node_slen > slen)) { 91762306a36Sopenharmony_ci slen = update_suffix(tn); 91862306a36Sopenharmony_ci if (node_slen == slen) 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci tn = node_parent(tn); 92262306a36Sopenharmony_ci node_slen = tn->slen; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic void node_push_suffix(struct key_vector *tn, unsigned char slen) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci while (tn->slen < slen) { 92962306a36Sopenharmony_ci tn->slen = slen; 93062306a36Sopenharmony_ci tn = node_parent(tn); 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */ 93562306a36Sopenharmony_cistatic struct key_vector *fib_find_node(struct trie *t, 93662306a36Sopenharmony_ci struct key_vector **tp, u32 key) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct key_vector *pn, *n = t->kv; 93962306a36Sopenharmony_ci unsigned long index = 0; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci do { 94262306a36Sopenharmony_ci pn = n; 94362306a36Sopenharmony_ci n = get_child_rcu(n, index); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (!n) 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci index = get_cindex(key, n); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* This bit of code is a bit tricky but it combines multiple 95162306a36Sopenharmony_ci * checks into a single check. The prefix consists of the 95262306a36Sopenharmony_ci * prefix plus zeros for the bits in the cindex. The index 95362306a36Sopenharmony_ci * is the difference between the key and this value. From 95462306a36Sopenharmony_ci * this we can actually derive several pieces of data. 95562306a36Sopenharmony_ci * if (index >= (1ul << bits)) 95662306a36Sopenharmony_ci * we have a mismatch in skip bits and failed 95762306a36Sopenharmony_ci * else 95862306a36Sopenharmony_ci * we know the value is cindex 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * This check is safe even if bits == KEYLENGTH due to the 96162306a36Sopenharmony_ci * fact that we can only allocate a node with 32 bits if a 96262306a36Sopenharmony_ci * long is greater than 32 bits. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci if (index >= (1ul << n->bits)) { 96562306a36Sopenharmony_ci n = NULL; 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* keep searching until we find a perfect match leaf or NULL */ 97062306a36Sopenharmony_ci } while (IS_TNODE(n)); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci *tp = pn; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return n; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* Return the first fib alias matching DSCP with 97862306a36Sopenharmony_ci * priority less than or equal to PRIO. 97962306a36Sopenharmony_ci * If 'find_first' is set, return the first matching 98062306a36Sopenharmony_ci * fib alias, regardless of DSCP and priority. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_cistatic struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, 98362306a36Sopenharmony_ci dscp_t dscp, u32 prio, u32 tb_id, 98462306a36Sopenharmony_ci bool find_first) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct fib_alias *fa; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (!fah) 98962306a36Sopenharmony_ci return NULL; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci hlist_for_each_entry(fa, fah, fa_list) { 99262306a36Sopenharmony_ci /* Avoid Sparse warning when using dscp_t in inequalities */ 99362306a36Sopenharmony_ci u8 __fa_dscp = inet_dscp_to_dsfield(fa->fa_dscp); 99462306a36Sopenharmony_ci u8 __dscp = inet_dscp_to_dsfield(dscp); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (fa->fa_slen < slen) 99762306a36Sopenharmony_ci continue; 99862306a36Sopenharmony_ci if (fa->fa_slen != slen) 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci if (fa->tb_id > tb_id) 100162306a36Sopenharmony_ci continue; 100262306a36Sopenharmony_ci if (fa->tb_id != tb_id) 100362306a36Sopenharmony_ci break; 100462306a36Sopenharmony_ci if (find_first) 100562306a36Sopenharmony_ci return fa; 100662306a36Sopenharmony_ci if (__fa_dscp > __dscp) 100762306a36Sopenharmony_ci continue; 100862306a36Sopenharmony_ci if (fa->fa_info->fib_priority >= prio || __fa_dscp < __dscp) 100962306a36Sopenharmony_ci return fa; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return NULL; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic struct fib_alias * 101662306a36Sopenharmony_cifib_find_matching_alias(struct net *net, const struct fib_rt_info *fri) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci u8 slen = KEYLENGTH - fri->dst_len; 101962306a36Sopenharmony_ci struct key_vector *l, *tp; 102062306a36Sopenharmony_ci struct fib_table *tb; 102162306a36Sopenharmony_ci struct fib_alias *fa; 102262306a36Sopenharmony_ci struct trie *t; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci tb = fib_get_table(net, fri->tb_id); 102562306a36Sopenharmony_ci if (!tb) 102662306a36Sopenharmony_ci return NULL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci t = (struct trie *)tb->tb_data; 102962306a36Sopenharmony_ci l = fib_find_node(t, &tp, be32_to_cpu(fri->dst)); 103062306a36Sopenharmony_ci if (!l) 103162306a36Sopenharmony_ci return NULL; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 103462306a36Sopenharmony_ci if (fa->fa_slen == slen && fa->tb_id == fri->tb_id && 103562306a36Sopenharmony_ci fa->fa_dscp == fri->dscp && fa->fa_info == fri->fi && 103662306a36Sopenharmony_ci fa->fa_type == fri->type) 103762306a36Sopenharmony_ci return fa; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci return NULL; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_civoid fib_alias_hw_flags_set(struct net *net, const struct fib_rt_info *fri) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci u8 fib_notify_on_flag_change; 104662306a36Sopenharmony_ci struct fib_alias *fa_match; 104762306a36Sopenharmony_ci struct sk_buff *skb; 104862306a36Sopenharmony_ci int err; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci rcu_read_lock(); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci fa_match = fib_find_matching_alias(net, fri); 105362306a36Sopenharmony_ci if (!fa_match) 105462306a36Sopenharmony_ci goto out; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* These are paired with the WRITE_ONCE() happening in this function. 105762306a36Sopenharmony_ci * The reason is that we are only protected by RCU at this point. 105862306a36Sopenharmony_ci */ 105962306a36Sopenharmony_ci if (READ_ONCE(fa_match->offload) == fri->offload && 106062306a36Sopenharmony_ci READ_ONCE(fa_match->trap) == fri->trap && 106162306a36Sopenharmony_ci READ_ONCE(fa_match->offload_failed) == fri->offload_failed) 106262306a36Sopenharmony_ci goto out; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci WRITE_ONCE(fa_match->offload, fri->offload); 106562306a36Sopenharmony_ci WRITE_ONCE(fa_match->trap, fri->trap); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci fib_notify_on_flag_change = READ_ONCE(net->ipv4.sysctl_fib_notify_on_flag_change); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 2 means send notifications only if offload_failed was changed. */ 107062306a36Sopenharmony_ci if (fib_notify_on_flag_change == 2 && 107162306a36Sopenharmony_ci READ_ONCE(fa_match->offload_failed) == fri->offload_failed) 107262306a36Sopenharmony_ci goto out; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci WRITE_ONCE(fa_match->offload_failed, fri->offload_failed); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (!fib_notify_on_flag_change) 107762306a36Sopenharmony_ci goto out; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci skb = nlmsg_new(fib_nlmsg_size(fa_match->fa_info), GFP_ATOMIC); 108062306a36Sopenharmony_ci if (!skb) { 108162306a36Sopenharmony_ci err = -ENOBUFS; 108262306a36Sopenharmony_ci goto errout; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci err = fib_dump_info(skb, 0, 0, RTM_NEWROUTE, fri, 0); 108662306a36Sopenharmony_ci if (err < 0) { 108762306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ 108862306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 108962306a36Sopenharmony_ci kfree_skb(skb); 109062306a36Sopenharmony_ci goto errout; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV4_ROUTE, NULL, GFP_ATOMIC); 109462306a36Sopenharmony_ci goto out; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cierrout: 109762306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV4_ROUTE, err); 109862306a36Sopenharmony_ciout: 109962306a36Sopenharmony_ci rcu_read_unlock(); 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_alias_hw_flags_set); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic void trie_rebalance(struct trie *t, struct key_vector *tn) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci while (!IS_TRIE(tn)) 110662306a36Sopenharmony_ci tn = resize(t, tn); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int fib_insert_node(struct trie *t, struct key_vector *tp, 111062306a36Sopenharmony_ci struct fib_alias *new, t_key key) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct key_vector *n, *l; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci l = leaf_new(key, new); 111562306a36Sopenharmony_ci if (!l) 111662306a36Sopenharmony_ci goto noleaf; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* retrieve child from parent node */ 111962306a36Sopenharmony_ci n = get_child(tp, get_index(key, tp)); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* Case 2: n is a LEAF or a TNODE and the key doesn't match. 112262306a36Sopenharmony_ci * 112362306a36Sopenharmony_ci * Add a new tnode here 112462306a36Sopenharmony_ci * first tnode need some special handling 112562306a36Sopenharmony_ci * leaves us in position for handling as case 3 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci if (n) { 112862306a36Sopenharmony_ci struct key_vector *tn; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci tn = tnode_new(key, __fls(key ^ n->key), 1); 113162306a36Sopenharmony_ci if (!tn) 113262306a36Sopenharmony_ci goto notnode; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* initialize routes out of node */ 113562306a36Sopenharmony_ci NODE_INIT_PARENT(tn, tp); 113662306a36Sopenharmony_ci put_child(tn, get_index(key, tn) ^ 1, n); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* start adding routes into the node */ 113962306a36Sopenharmony_ci put_child_root(tp, key, tn); 114062306a36Sopenharmony_ci node_set_parent(n, tn); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* parent now has a NULL spot where the leaf can go */ 114362306a36Sopenharmony_ci tp = tn; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Case 3: n is NULL, and will just insert a new leaf */ 114762306a36Sopenharmony_ci node_push_suffix(tp, new->fa_slen); 114862306a36Sopenharmony_ci NODE_INIT_PARENT(l, tp); 114962306a36Sopenharmony_ci put_child_root(tp, key, l); 115062306a36Sopenharmony_ci trie_rebalance(t, tp); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_cinotnode: 115462306a36Sopenharmony_ci node_free(l); 115562306a36Sopenharmony_cinoleaf: 115662306a36Sopenharmony_ci return -ENOMEM; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int fib_insert_alias(struct trie *t, struct key_vector *tp, 116062306a36Sopenharmony_ci struct key_vector *l, struct fib_alias *new, 116162306a36Sopenharmony_ci struct fib_alias *fa, t_key key) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci if (!l) 116462306a36Sopenharmony_ci return fib_insert_node(t, tp, new, key); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (fa) { 116762306a36Sopenharmony_ci hlist_add_before_rcu(&new->fa_list, &fa->fa_list); 116862306a36Sopenharmony_ci } else { 116962306a36Sopenharmony_ci struct fib_alias *last; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci hlist_for_each_entry(last, &l->leaf, fa_list) { 117262306a36Sopenharmony_ci if (new->fa_slen < last->fa_slen) 117362306a36Sopenharmony_ci break; 117462306a36Sopenharmony_ci if ((new->fa_slen == last->fa_slen) && 117562306a36Sopenharmony_ci (new->tb_id > last->tb_id)) 117662306a36Sopenharmony_ci break; 117762306a36Sopenharmony_ci fa = last; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (fa) 118162306a36Sopenharmony_ci hlist_add_behind_rcu(&new->fa_list, &fa->fa_list); 118262306a36Sopenharmony_ci else 118362306a36Sopenharmony_ci hlist_add_head_rcu(&new->fa_list, &l->leaf); 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* if we added to the tail node then we need to update slen */ 118762306a36Sopenharmony_ci if (l->slen < new->fa_slen) { 118862306a36Sopenharmony_ci l->slen = new->fa_slen; 118962306a36Sopenharmony_ci node_push_suffix(tp, new->fa_slen); 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci return 0; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci if (plen > KEYLENGTH) { 119862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid prefix length"); 119962306a36Sopenharmony_ci return false; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if ((plen < KEYLENGTH) && (key << plen)) { 120362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 120462306a36Sopenharmony_ci "Invalid prefix for given prefix length"); 120562306a36Sopenharmony_ci return false; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return true; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp, 121262306a36Sopenharmony_ci struct key_vector *l, struct fib_alias *old); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci/* Caller must hold RTNL. */ 121562306a36Sopenharmony_ciint fib_table_insert(struct net *net, struct fib_table *tb, 121662306a36Sopenharmony_ci struct fib_config *cfg, struct netlink_ext_ack *extack) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 121962306a36Sopenharmony_ci struct fib_alias *fa, *new_fa; 122062306a36Sopenharmony_ci struct key_vector *l, *tp; 122162306a36Sopenharmony_ci u16 nlflags = NLM_F_EXCL; 122262306a36Sopenharmony_ci struct fib_info *fi; 122362306a36Sopenharmony_ci u8 plen = cfg->fc_dst_len; 122462306a36Sopenharmony_ci u8 slen = KEYLENGTH - plen; 122562306a36Sopenharmony_ci dscp_t dscp; 122662306a36Sopenharmony_ci u32 key; 122762306a36Sopenharmony_ci int err; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci key = ntohl(cfg->fc_dst); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (!fib_valid_key_len(key, plen, extack)) 123262306a36Sopenharmony_ci return -EINVAL; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci fi = fib_create_info(cfg, extack); 123762306a36Sopenharmony_ci if (IS_ERR(fi)) { 123862306a36Sopenharmony_ci err = PTR_ERR(fi); 123962306a36Sopenharmony_ci goto err; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci dscp = cfg->fc_dscp; 124362306a36Sopenharmony_ci l = fib_find_node(t, &tp, key); 124462306a36Sopenharmony_ci fa = l ? fib_find_alias(&l->leaf, slen, dscp, fi->fib_priority, 124562306a36Sopenharmony_ci tb->tb_id, false) : NULL; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* Now fa, if non-NULL, points to the first fib alias 124862306a36Sopenharmony_ci * with the same keys [prefix,dscp,priority], if such key already 124962306a36Sopenharmony_ci * exists or to the node before which we will insert new one. 125062306a36Sopenharmony_ci * 125162306a36Sopenharmony_ci * If fa is NULL, we will need to allocate a new one and 125262306a36Sopenharmony_ci * insert to the tail of the section matching the suffix length 125362306a36Sopenharmony_ci * of the new alias. 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (fa && fa->fa_dscp == dscp && 125762306a36Sopenharmony_ci fa->fa_info->fib_priority == fi->fib_priority) { 125862306a36Sopenharmony_ci struct fib_alias *fa_first, *fa_match; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci err = -EEXIST; 126162306a36Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_EXCL) 126262306a36Sopenharmony_ci goto out; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci nlflags &= ~NLM_F_EXCL; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* We have 2 goals: 126762306a36Sopenharmony_ci * 1. Find exact match for type, scope, fib_info to avoid 126862306a36Sopenharmony_ci * duplicate routes 126962306a36Sopenharmony_ci * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it 127062306a36Sopenharmony_ci */ 127162306a36Sopenharmony_ci fa_match = NULL; 127262306a36Sopenharmony_ci fa_first = fa; 127362306a36Sopenharmony_ci hlist_for_each_entry_from(fa, fa_list) { 127462306a36Sopenharmony_ci if ((fa->fa_slen != slen) || 127562306a36Sopenharmony_ci (fa->tb_id != tb->tb_id) || 127662306a36Sopenharmony_ci (fa->fa_dscp != dscp)) 127762306a36Sopenharmony_ci break; 127862306a36Sopenharmony_ci if (fa->fa_info->fib_priority != fi->fib_priority) 127962306a36Sopenharmony_ci break; 128062306a36Sopenharmony_ci if (fa->fa_type == cfg->fc_type && 128162306a36Sopenharmony_ci fa->fa_info == fi) { 128262306a36Sopenharmony_ci fa_match = fa; 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_REPLACE) { 128862306a36Sopenharmony_ci struct fib_info *fi_drop; 128962306a36Sopenharmony_ci u8 state; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci nlflags |= NLM_F_REPLACE; 129262306a36Sopenharmony_ci fa = fa_first; 129362306a36Sopenharmony_ci if (fa_match) { 129462306a36Sopenharmony_ci if (fa == fa_match) 129562306a36Sopenharmony_ci err = 0; 129662306a36Sopenharmony_ci goto out; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci err = -ENOBUFS; 129962306a36Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 130062306a36Sopenharmony_ci if (!new_fa) 130162306a36Sopenharmony_ci goto out; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci fi_drop = fa->fa_info; 130462306a36Sopenharmony_ci new_fa->fa_dscp = fa->fa_dscp; 130562306a36Sopenharmony_ci new_fa->fa_info = fi; 130662306a36Sopenharmony_ci new_fa->fa_type = cfg->fc_type; 130762306a36Sopenharmony_ci state = fa->fa_state; 130862306a36Sopenharmony_ci new_fa->fa_state = state & ~FA_S_ACCESSED; 130962306a36Sopenharmony_ci new_fa->fa_slen = fa->fa_slen; 131062306a36Sopenharmony_ci new_fa->tb_id = tb->tb_id; 131162306a36Sopenharmony_ci new_fa->fa_default = -1; 131262306a36Sopenharmony_ci new_fa->offload = 0; 131362306a36Sopenharmony_ci new_fa->trap = 0; 131462306a36Sopenharmony_ci new_fa->offload_failed = 0; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (fib_find_alias(&l->leaf, fa->fa_slen, 0, 0, 131962306a36Sopenharmony_ci tb->tb_id, true) == new_fa) { 132062306a36Sopenharmony_ci enum fib_event_type fib_event; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 132362306a36Sopenharmony_ci err = call_fib_entry_notifiers(net, fib_event, 132462306a36Sopenharmony_ci key, plen, 132562306a36Sopenharmony_ci new_fa, extack); 132662306a36Sopenharmony_ci if (err) { 132762306a36Sopenharmony_ci hlist_replace_rcu(&new_fa->fa_list, 132862306a36Sopenharmony_ci &fa->fa_list); 132962306a36Sopenharmony_ci goto out_free_new_fa; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, 133462306a36Sopenharmony_ci tb->tb_id, &cfg->fc_nlinfo, nlflags); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci alias_free_mem_rcu(fa); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci fib_release_info(fi_drop); 133962306a36Sopenharmony_ci if (state & FA_S_ACCESSED) 134062306a36Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci goto succeeded; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci /* Error if we find a perfect match which 134562306a36Sopenharmony_ci * uses the same scope, type, and nexthop 134662306a36Sopenharmony_ci * information. 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ci if (fa_match) 134962306a36Sopenharmony_ci goto out; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (cfg->fc_nlflags & NLM_F_APPEND) 135262306a36Sopenharmony_ci nlflags |= NLM_F_APPEND; 135362306a36Sopenharmony_ci else 135462306a36Sopenharmony_ci fa = fa_first; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci err = -ENOENT; 135762306a36Sopenharmony_ci if (!(cfg->fc_nlflags & NLM_F_CREATE)) 135862306a36Sopenharmony_ci goto out; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci nlflags |= NLM_F_CREATE; 136162306a36Sopenharmony_ci err = -ENOBUFS; 136262306a36Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 136362306a36Sopenharmony_ci if (!new_fa) 136462306a36Sopenharmony_ci goto out; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci new_fa->fa_info = fi; 136762306a36Sopenharmony_ci new_fa->fa_dscp = dscp; 136862306a36Sopenharmony_ci new_fa->fa_type = cfg->fc_type; 136962306a36Sopenharmony_ci new_fa->fa_state = 0; 137062306a36Sopenharmony_ci new_fa->fa_slen = slen; 137162306a36Sopenharmony_ci new_fa->tb_id = tb->tb_id; 137262306a36Sopenharmony_ci new_fa->fa_default = -1; 137362306a36Sopenharmony_ci new_fa->offload = 0; 137462306a36Sopenharmony_ci new_fa->trap = 0; 137562306a36Sopenharmony_ci new_fa->offload_failed = 0; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* Insert new entry to the list. */ 137862306a36Sopenharmony_ci err = fib_insert_alias(t, tp, l, new_fa, fa, key); 137962306a36Sopenharmony_ci if (err) 138062306a36Sopenharmony_ci goto out_free_new_fa; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* The alias was already inserted, so the node must exist. */ 138362306a36Sopenharmony_ci l = l ? l : fib_find_node(t, &tp, key); 138462306a36Sopenharmony_ci if (WARN_ON_ONCE(!l)) { 138562306a36Sopenharmony_ci err = -ENOENT; 138662306a36Sopenharmony_ci goto out_free_new_fa; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (fib_find_alias(&l->leaf, new_fa->fa_slen, 0, 0, tb->tb_id, true) == 139062306a36Sopenharmony_ci new_fa) { 139162306a36Sopenharmony_ci enum fib_event_type fib_event; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 139462306a36Sopenharmony_ci err = call_fib_entry_notifiers(net, fib_event, key, plen, 139562306a36Sopenharmony_ci new_fa, extack); 139662306a36Sopenharmony_ci if (err) 139762306a36Sopenharmony_ci goto out_remove_new_fa; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci if (!plen) 140162306a36Sopenharmony_ci tb->tb_num_default++; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 140462306a36Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, 140562306a36Sopenharmony_ci &cfg->fc_nlinfo, nlflags); 140662306a36Sopenharmony_cisucceeded: 140762306a36Sopenharmony_ci return 0; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ciout_remove_new_fa: 141062306a36Sopenharmony_ci fib_remove_alias(t, tp, l, new_fa); 141162306a36Sopenharmony_ciout_free_new_fa: 141262306a36Sopenharmony_ci kmem_cache_free(fn_alias_kmem, new_fa); 141362306a36Sopenharmony_ciout: 141462306a36Sopenharmony_ci fib_release_info(fi); 141562306a36Sopenharmony_cierr: 141662306a36Sopenharmony_ci return err; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic inline t_key prefix_mismatch(t_key key, struct key_vector *n) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci t_key prefix = n->key; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return (key ^ prefix) & (prefix | -prefix); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cibool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags, 142762306a36Sopenharmony_ci const struct flowi4 *flp) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci if (nhc->nhc_flags & RTNH_F_DEAD) 143062306a36Sopenharmony_ci return false; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (ip_ignore_linkdown(nhc->nhc_dev) && 143362306a36Sopenharmony_ci nhc->nhc_flags & RTNH_F_LINKDOWN && 143462306a36Sopenharmony_ci !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE)) 143562306a36Sopenharmony_ci return false; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (flp->flowi4_oif && flp->flowi4_oif != nhc->nhc_oif) 143862306a36Sopenharmony_ci return false; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci return true; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci/* should be called with rcu_read_lock */ 144462306a36Sopenharmony_ciint fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, 144562306a36Sopenharmony_ci struct fib_result *res, int fib_flags) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 144862306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 144962306a36Sopenharmony_ci struct trie_use_stats __percpu *stats = t->stats; 145062306a36Sopenharmony_ci#endif 145162306a36Sopenharmony_ci const t_key key = ntohl(flp->daddr); 145262306a36Sopenharmony_ci struct key_vector *n, *pn; 145362306a36Sopenharmony_ci struct fib_alias *fa; 145462306a36Sopenharmony_ci unsigned long index; 145562306a36Sopenharmony_ci t_key cindex; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci pn = t->kv; 145862306a36Sopenharmony_ci cindex = 0; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci n = get_child_rcu(pn, cindex); 146162306a36Sopenharmony_ci if (!n) { 146262306a36Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, NULL, -EAGAIN); 146362306a36Sopenharmony_ci return -EAGAIN; 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 146762306a36Sopenharmony_ci this_cpu_inc(stats->gets); 146862306a36Sopenharmony_ci#endif 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* Step 1: Travel to the longest prefix match in the trie */ 147162306a36Sopenharmony_ci for (;;) { 147262306a36Sopenharmony_ci index = get_cindex(key, n); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* This bit of code is a bit tricky but it combines multiple 147562306a36Sopenharmony_ci * checks into a single check. The prefix consists of the 147662306a36Sopenharmony_ci * prefix plus zeros for the "bits" in the prefix. The index 147762306a36Sopenharmony_ci * is the difference between the key and this value. From 147862306a36Sopenharmony_ci * this we can actually derive several pieces of data. 147962306a36Sopenharmony_ci * if (index >= (1ul << bits)) 148062306a36Sopenharmony_ci * we have a mismatch in skip bits and failed 148162306a36Sopenharmony_ci * else 148262306a36Sopenharmony_ci * we know the value is cindex 148362306a36Sopenharmony_ci * 148462306a36Sopenharmony_ci * This check is safe even if bits == KEYLENGTH due to the 148562306a36Sopenharmony_ci * fact that we can only allocate a node with 32 bits if a 148662306a36Sopenharmony_ci * long is greater than 32 bits. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_ci if (index >= (1ul << n->bits)) 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* we have found a leaf. Prefixes have already been compared */ 149262306a36Sopenharmony_ci if (IS_LEAF(n)) 149362306a36Sopenharmony_ci goto found; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci /* only record pn and cindex if we are going to be chopping 149662306a36Sopenharmony_ci * bits later. Otherwise we are just wasting cycles. 149762306a36Sopenharmony_ci */ 149862306a36Sopenharmony_ci if (n->slen > n->pos) { 149962306a36Sopenharmony_ci pn = n; 150062306a36Sopenharmony_ci cindex = index; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci n = get_child_rcu(n, index); 150462306a36Sopenharmony_ci if (unlikely(!n)) 150562306a36Sopenharmony_ci goto backtrace; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci /* Step 2: Sort out leaves and begin backtracing for longest prefix */ 150962306a36Sopenharmony_ci for (;;) { 151062306a36Sopenharmony_ci /* record the pointer where our next node pointer is stored */ 151162306a36Sopenharmony_ci struct key_vector __rcu **cptr = n->tnode; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci /* This test verifies that none of the bits that differ 151462306a36Sopenharmony_ci * between the key and the prefix exist in the region of 151562306a36Sopenharmony_ci * the lsb and higher in the prefix. 151662306a36Sopenharmony_ci */ 151762306a36Sopenharmony_ci if (unlikely(prefix_mismatch(key, n)) || (n->slen == n->pos)) 151862306a36Sopenharmony_ci goto backtrace; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* exit out and process leaf */ 152162306a36Sopenharmony_ci if (unlikely(IS_LEAF(n))) 152262306a36Sopenharmony_ci break; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* Don't bother recording parent info. Since we are in 152562306a36Sopenharmony_ci * prefix match mode we will have to come back to wherever 152662306a36Sopenharmony_ci * we started this traversal anyway 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci while ((n = rcu_dereference(*cptr)) == NULL) { 153062306a36Sopenharmony_cibacktrace: 153162306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 153262306a36Sopenharmony_ci if (!n) 153362306a36Sopenharmony_ci this_cpu_inc(stats->null_node_hit); 153462306a36Sopenharmony_ci#endif 153562306a36Sopenharmony_ci /* If we are at cindex 0 there are no more bits for 153662306a36Sopenharmony_ci * us to strip at this level so we must ascend back 153762306a36Sopenharmony_ci * up one level to see if there are any more bits to 153862306a36Sopenharmony_ci * be stripped there. 153962306a36Sopenharmony_ci */ 154062306a36Sopenharmony_ci while (!cindex) { 154162306a36Sopenharmony_ci t_key pkey = pn->key; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* If we don't have a parent then there is 154462306a36Sopenharmony_ci * nothing for us to do as we do not have any 154562306a36Sopenharmony_ci * further nodes to parse. 154662306a36Sopenharmony_ci */ 154762306a36Sopenharmony_ci if (IS_TRIE(pn)) { 154862306a36Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, 154962306a36Sopenharmony_ci NULL, -EAGAIN); 155062306a36Sopenharmony_ci return -EAGAIN; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 155362306a36Sopenharmony_ci this_cpu_inc(stats->backtrack); 155462306a36Sopenharmony_ci#endif 155562306a36Sopenharmony_ci /* Get Child's index */ 155662306a36Sopenharmony_ci pn = node_parent_rcu(pn); 155762306a36Sopenharmony_ci cindex = get_index(pkey, pn); 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci /* strip the least significant bit from the cindex */ 156162306a36Sopenharmony_ci cindex &= cindex - 1; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci /* grab pointer for next child node */ 156462306a36Sopenharmony_ci cptr = &pn->tnode[cindex]; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cifound: 156962306a36Sopenharmony_ci /* this line carries forward the xor from earlier in the function */ 157062306a36Sopenharmony_ci index = key ^ n->key; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* Step 3: Process the leaf, if that fails fall back to backtracing */ 157362306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) { 157462306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 157562306a36Sopenharmony_ci struct fib_nh_common *nhc; 157662306a36Sopenharmony_ci int nhsel, err; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci if ((BITS_PER_LONG > KEYLENGTH) || (fa->fa_slen < KEYLENGTH)) { 157962306a36Sopenharmony_ci if (index >= (1ul << fa->fa_slen)) 158062306a36Sopenharmony_ci continue; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci if (fa->fa_dscp && 158362306a36Sopenharmony_ci inet_dscp_to_dsfield(fa->fa_dscp) != flp->flowi4_tos) 158462306a36Sopenharmony_ci continue; 158562306a36Sopenharmony_ci /* Paired with WRITE_ONCE() in fib_release_info() */ 158662306a36Sopenharmony_ci if (READ_ONCE(fi->fib_dead)) 158762306a36Sopenharmony_ci continue; 158862306a36Sopenharmony_ci if (fa->fa_info->fib_scope < flp->flowi4_scope) 158962306a36Sopenharmony_ci continue; 159062306a36Sopenharmony_ci fib_alias_accessed(fa); 159162306a36Sopenharmony_ci err = fib_props[fa->fa_type].error; 159262306a36Sopenharmony_ci if (unlikely(err < 0)) { 159362306a36Sopenharmony_ciout_reject: 159462306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 159562306a36Sopenharmony_ci this_cpu_inc(stats->semantic_match_passed); 159662306a36Sopenharmony_ci#endif 159762306a36Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, NULL, err); 159862306a36Sopenharmony_ci return err; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci if (fi->fib_flags & RTNH_F_DEAD) 160162306a36Sopenharmony_ci continue; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (unlikely(fi->nh)) { 160462306a36Sopenharmony_ci if (nexthop_is_blackhole(fi->nh)) { 160562306a36Sopenharmony_ci err = fib_props[RTN_BLACKHOLE].error; 160662306a36Sopenharmony_ci goto out_reject; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci nhc = nexthop_get_nhc_lookup(fi->nh, fib_flags, flp, 161062306a36Sopenharmony_ci &nhsel); 161162306a36Sopenharmony_ci if (nhc) 161262306a36Sopenharmony_ci goto set_result; 161362306a36Sopenharmony_ci goto miss; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) { 161762306a36Sopenharmony_ci nhc = fib_info_nhc(fi, nhsel); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (!fib_lookup_good_nhc(nhc, fib_flags, flp)) 162062306a36Sopenharmony_ci continue; 162162306a36Sopenharmony_ciset_result: 162262306a36Sopenharmony_ci if (!(fib_flags & FIB_LOOKUP_NOREF)) 162362306a36Sopenharmony_ci refcount_inc(&fi->fib_clntref); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci res->prefix = htonl(n->key); 162662306a36Sopenharmony_ci res->prefixlen = KEYLENGTH - fa->fa_slen; 162762306a36Sopenharmony_ci res->nh_sel = nhsel; 162862306a36Sopenharmony_ci res->nhc = nhc; 162962306a36Sopenharmony_ci res->type = fa->fa_type; 163062306a36Sopenharmony_ci res->scope = fi->fib_scope; 163162306a36Sopenharmony_ci res->fi = fi; 163262306a36Sopenharmony_ci res->table = tb; 163362306a36Sopenharmony_ci res->fa_head = &n->leaf; 163462306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 163562306a36Sopenharmony_ci this_cpu_inc(stats->semantic_match_passed); 163662306a36Sopenharmony_ci#endif 163762306a36Sopenharmony_ci trace_fib_table_lookup(tb->tb_id, flp, nhc, err); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return err; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_cimiss: 164362306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 164462306a36Sopenharmony_ci this_cpu_inc(stats->semantic_match_miss); 164562306a36Sopenharmony_ci#endif 164662306a36Sopenharmony_ci goto backtrace; 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_table_lookup); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp, 165162306a36Sopenharmony_ci struct key_vector *l, struct fib_alias *old) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci /* record the location of the previous list_info entry */ 165462306a36Sopenharmony_ci struct hlist_node **pprev = old->fa_list.pprev; 165562306a36Sopenharmony_ci struct fib_alias *fa = hlist_entry(pprev, typeof(*fa), fa_list.next); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci /* remove the fib_alias from the list */ 165862306a36Sopenharmony_ci hlist_del_rcu(&old->fa_list); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* if we emptied the list this leaf will be freed and we can sort 166162306a36Sopenharmony_ci * out parent suffix lengths as a part of trie_rebalance 166262306a36Sopenharmony_ci */ 166362306a36Sopenharmony_ci if (hlist_empty(&l->leaf)) { 166462306a36Sopenharmony_ci if (tp->slen == l->slen) 166562306a36Sopenharmony_ci node_pull_suffix(tp, tp->pos); 166662306a36Sopenharmony_ci put_child_root(tp, l->key, NULL); 166762306a36Sopenharmony_ci node_free(l); 166862306a36Sopenharmony_ci trie_rebalance(t, tp); 166962306a36Sopenharmony_ci return; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci /* only access fa if it is pointing at the last valid hlist_node */ 167362306a36Sopenharmony_ci if (*pprev) 167462306a36Sopenharmony_ci return; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci /* update the trie with the latest suffix length */ 167762306a36Sopenharmony_ci l->slen = fa->fa_slen; 167862306a36Sopenharmony_ci node_pull_suffix(tp, fa->fa_slen); 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic void fib_notify_alias_delete(struct net *net, u32 key, 168262306a36Sopenharmony_ci struct hlist_head *fah, 168362306a36Sopenharmony_ci struct fib_alias *fa_to_delete, 168462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci struct fib_alias *fa_next, *fa_to_notify; 168762306a36Sopenharmony_ci u32 tb_id = fa_to_delete->tb_id; 168862306a36Sopenharmony_ci u8 slen = fa_to_delete->fa_slen; 168962306a36Sopenharmony_ci enum fib_event_type fib_event; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci /* Do not notify if we do not care about the route. */ 169262306a36Sopenharmony_ci if (fib_find_alias(fah, slen, 0, 0, tb_id, true) != fa_to_delete) 169362306a36Sopenharmony_ci return; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci /* Determine if the route should be replaced by the next route in the 169662306a36Sopenharmony_ci * list. 169762306a36Sopenharmony_ci */ 169862306a36Sopenharmony_ci fa_next = hlist_entry_safe(fa_to_delete->fa_list.next, 169962306a36Sopenharmony_ci struct fib_alias, fa_list); 170062306a36Sopenharmony_ci if (fa_next && fa_next->fa_slen == slen && fa_next->tb_id == tb_id) { 170162306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_REPLACE; 170262306a36Sopenharmony_ci fa_to_notify = fa_next; 170362306a36Sopenharmony_ci } else { 170462306a36Sopenharmony_ci fib_event = FIB_EVENT_ENTRY_DEL; 170562306a36Sopenharmony_ci fa_to_notify = fa_to_delete; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci call_fib_entry_notifiers(net, fib_event, key, KEYLENGTH - slen, 170862306a36Sopenharmony_ci fa_to_notify, extack); 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci/* Caller must hold RTNL. */ 171262306a36Sopenharmony_ciint fib_table_delete(struct net *net, struct fib_table *tb, 171362306a36Sopenharmony_ci struct fib_config *cfg, struct netlink_ext_ack *extack) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 171662306a36Sopenharmony_ci struct fib_alias *fa, *fa_to_delete; 171762306a36Sopenharmony_ci struct key_vector *l, *tp; 171862306a36Sopenharmony_ci u8 plen = cfg->fc_dst_len; 171962306a36Sopenharmony_ci u8 slen = KEYLENGTH - plen; 172062306a36Sopenharmony_ci dscp_t dscp; 172162306a36Sopenharmony_ci u32 key; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci key = ntohl(cfg->fc_dst); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (!fib_valid_key_len(key, plen, extack)) 172662306a36Sopenharmony_ci return -EINVAL; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci l = fib_find_node(t, &tp, key); 172962306a36Sopenharmony_ci if (!l) 173062306a36Sopenharmony_ci return -ESRCH; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci dscp = cfg->fc_dscp; 173362306a36Sopenharmony_ci fa = fib_find_alias(&l->leaf, slen, dscp, 0, tb->tb_id, false); 173462306a36Sopenharmony_ci if (!fa) 173562306a36Sopenharmony_ci return -ESRCH; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci pr_debug("Deleting %08x/%d dsfield=0x%02x t=%p\n", key, plen, 173862306a36Sopenharmony_ci inet_dscp_to_dsfield(dscp), t); 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci fa_to_delete = NULL; 174162306a36Sopenharmony_ci hlist_for_each_entry_from(fa, fa_list) { 174262306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if ((fa->fa_slen != slen) || 174562306a36Sopenharmony_ci (fa->tb_id != tb->tb_id) || 174662306a36Sopenharmony_ci (fa->fa_dscp != dscp)) 174762306a36Sopenharmony_ci break; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) && 175062306a36Sopenharmony_ci (cfg->fc_scope == RT_SCOPE_NOWHERE || 175162306a36Sopenharmony_ci fa->fa_info->fib_scope == cfg->fc_scope) && 175262306a36Sopenharmony_ci (!cfg->fc_prefsrc || 175362306a36Sopenharmony_ci fi->fib_prefsrc == cfg->fc_prefsrc) && 175462306a36Sopenharmony_ci (!cfg->fc_protocol || 175562306a36Sopenharmony_ci fi->fib_protocol == cfg->fc_protocol) && 175662306a36Sopenharmony_ci fib_nh_match(net, cfg, fi, extack) == 0 && 175762306a36Sopenharmony_ci fib_metrics_match(cfg, fi)) { 175862306a36Sopenharmony_ci fa_to_delete = fa; 175962306a36Sopenharmony_ci break; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (!fa_to_delete) 176462306a36Sopenharmony_ci return -ESRCH; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci fib_notify_alias_delete(net, key, &l->leaf, fa_to_delete, extack); 176762306a36Sopenharmony_ci rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, 176862306a36Sopenharmony_ci &cfg->fc_nlinfo, 0); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (!plen) 177162306a36Sopenharmony_ci tb->tb_num_default--; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci fib_remove_alias(t, tp, l, fa_to_delete); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (fa_to_delete->fa_state & FA_S_ACCESSED) 177662306a36Sopenharmony_ci rt_cache_flush(cfg->fc_nlinfo.nl_net); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci fib_release_info(fa_to_delete->fa_info); 177962306a36Sopenharmony_ci alias_free_mem_rcu(fa_to_delete); 178062306a36Sopenharmony_ci return 0; 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci/* Scan for the next leaf starting at the provided key value */ 178462306a36Sopenharmony_cistatic struct key_vector *leaf_walk_rcu(struct key_vector **tn, t_key key) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci struct key_vector *pn, *n = *tn; 178762306a36Sopenharmony_ci unsigned long cindex; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci /* this loop is meant to try and find the key in the trie */ 179062306a36Sopenharmony_ci do { 179162306a36Sopenharmony_ci /* record parent and next child index */ 179262306a36Sopenharmony_ci pn = n; 179362306a36Sopenharmony_ci cindex = (key > pn->key) ? get_index(key, pn) : 0; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (cindex >> pn->bits) 179662306a36Sopenharmony_ci break; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* descend into the next child */ 179962306a36Sopenharmony_ci n = get_child_rcu(pn, cindex++); 180062306a36Sopenharmony_ci if (!n) 180162306a36Sopenharmony_ci break; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci /* guarantee forward progress on the keys */ 180462306a36Sopenharmony_ci if (IS_LEAF(n) && (n->key >= key)) 180562306a36Sopenharmony_ci goto found; 180662306a36Sopenharmony_ci } while (IS_TNODE(n)); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci /* this loop will search for the next leaf with a greater key */ 180962306a36Sopenharmony_ci while (!IS_TRIE(pn)) { 181062306a36Sopenharmony_ci /* if we exhausted the parent node we will need to climb */ 181162306a36Sopenharmony_ci if (cindex >= (1ul << pn->bits)) { 181262306a36Sopenharmony_ci t_key pkey = pn->key; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci pn = node_parent_rcu(pn); 181562306a36Sopenharmony_ci cindex = get_index(pkey, pn) + 1; 181662306a36Sopenharmony_ci continue; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* grab the next available node */ 182062306a36Sopenharmony_ci n = get_child_rcu(pn, cindex++); 182162306a36Sopenharmony_ci if (!n) 182262306a36Sopenharmony_ci continue; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* no need to compare keys since we bumped the index */ 182562306a36Sopenharmony_ci if (IS_LEAF(n)) 182662306a36Sopenharmony_ci goto found; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* Rescan start scanning in new node */ 182962306a36Sopenharmony_ci pn = n; 183062306a36Sopenharmony_ci cindex = 0; 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci *tn = pn; 183462306a36Sopenharmony_ci return NULL; /* Root of trie */ 183562306a36Sopenharmony_cifound: 183662306a36Sopenharmony_ci /* if we are at the limit for keys just return NULL for the tnode */ 183762306a36Sopenharmony_ci *tn = pn; 183862306a36Sopenharmony_ci return n; 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_cistatic void fib_trie_free(struct fib_table *tb) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 184462306a36Sopenharmony_ci struct key_vector *pn = t->kv; 184562306a36Sopenharmony_ci unsigned long cindex = 1; 184662306a36Sopenharmony_ci struct hlist_node *tmp; 184762306a36Sopenharmony_ci struct fib_alias *fa; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* walk trie in reverse order and free everything */ 185062306a36Sopenharmony_ci for (;;) { 185162306a36Sopenharmony_ci struct key_vector *n; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (!(cindex--)) { 185462306a36Sopenharmony_ci t_key pkey = pn->key; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci if (IS_TRIE(pn)) 185762306a36Sopenharmony_ci break; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci n = pn; 186062306a36Sopenharmony_ci pn = node_parent(pn); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci /* drop emptied tnode */ 186362306a36Sopenharmony_ci put_child_root(pn, n->key, NULL); 186462306a36Sopenharmony_ci node_free(n); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci cindex = get_index(pkey, pn); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci continue; 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* grab the next available node */ 187262306a36Sopenharmony_ci n = get_child(pn, cindex); 187362306a36Sopenharmony_ci if (!n) 187462306a36Sopenharmony_ci continue; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (IS_TNODE(n)) { 187762306a36Sopenharmony_ci /* record pn and cindex for leaf walking */ 187862306a36Sopenharmony_ci pn = n; 187962306a36Sopenharmony_ci cindex = 1ul << n->bits; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci continue; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 188562306a36Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 188662306a36Sopenharmony_ci alias_free_mem_rcu(fa); 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci put_child_root(pn, n->key, NULL); 189062306a36Sopenharmony_ci node_free(n); 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 189462306a36Sopenharmony_ci free_percpu(t->stats); 189562306a36Sopenharmony_ci#endif 189662306a36Sopenharmony_ci kfree(tb); 189762306a36Sopenharmony_ci} 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_cistruct fib_table *fib_trie_unmerge(struct fib_table *oldtb) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci struct trie *ot = (struct trie *)oldtb->tb_data; 190262306a36Sopenharmony_ci struct key_vector *l, *tp = ot->kv; 190362306a36Sopenharmony_ci struct fib_table *local_tb; 190462306a36Sopenharmony_ci struct fib_alias *fa; 190562306a36Sopenharmony_ci struct trie *lt; 190662306a36Sopenharmony_ci t_key key = 0; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if (oldtb->tb_data == oldtb->__data) 190962306a36Sopenharmony_ci return oldtb; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci local_tb = fib_trie_table(RT_TABLE_LOCAL, NULL); 191262306a36Sopenharmony_ci if (!local_tb) 191362306a36Sopenharmony_ci return NULL; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci lt = (struct trie *)local_tb->tb_data; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 191862306a36Sopenharmony_ci struct key_vector *local_l = NULL, *local_tp; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci hlist_for_each_entry(fa, &l->leaf, fa_list) { 192162306a36Sopenharmony_ci struct fib_alias *new_fa; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci if (local_tb->tb_id != fa->tb_id) 192462306a36Sopenharmony_ci continue; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* clone fa for new local table */ 192762306a36Sopenharmony_ci new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); 192862306a36Sopenharmony_ci if (!new_fa) 192962306a36Sopenharmony_ci goto out; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci memcpy(new_fa, fa, sizeof(*fa)); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci /* insert clone into table */ 193462306a36Sopenharmony_ci if (!local_l) 193562306a36Sopenharmony_ci local_l = fib_find_node(lt, &local_tp, l->key); 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci if (fib_insert_alias(lt, local_tp, local_l, new_fa, 193862306a36Sopenharmony_ci NULL, l->key)) { 193962306a36Sopenharmony_ci kmem_cache_free(fn_alias_kmem, new_fa); 194062306a36Sopenharmony_ci goto out; 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* stop loop if key wrapped back to 0 */ 194562306a36Sopenharmony_ci key = l->key + 1; 194662306a36Sopenharmony_ci if (key < l->key) 194762306a36Sopenharmony_ci break; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci return local_tb; 195162306a36Sopenharmony_ciout: 195262306a36Sopenharmony_ci fib_trie_free(local_tb); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci return NULL; 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci/* Caller must hold RTNL */ 195862306a36Sopenharmony_civoid fib_table_flush_external(struct fib_table *tb) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 196162306a36Sopenharmony_ci struct key_vector *pn = t->kv; 196262306a36Sopenharmony_ci unsigned long cindex = 1; 196362306a36Sopenharmony_ci struct hlist_node *tmp; 196462306a36Sopenharmony_ci struct fib_alias *fa; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* walk trie in reverse order */ 196762306a36Sopenharmony_ci for (;;) { 196862306a36Sopenharmony_ci unsigned char slen = 0; 196962306a36Sopenharmony_ci struct key_vector *n; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci if (!(cindex--)) { 197262306a36Sopenharmony_ci t_key pkey = pn->key; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* cannot resize the trie vector */ 197562306a36Sopenharmony_ci if (IS_TRIE(pn)) 197662306a36Sopenharmony_ci break; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci /* update the suffix to address pulled leaves */ 197962306a36Sopenharmony_ci if (pn->slen > pn->pos) 198062306a36Sopenharmony_ci update_suffix(pn); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci /* resize completed node */ 198362306a36Sopenharmony_ci pn = resize(t, pn); 198462306a36Sopenharmony_ci cindex = get_index(pkey, pn); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci continue; 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* grab the next available node */ 199062306a36Sopenharmony_ci n = get_child(pn, cindex); 199162306a36Sopenharmony_ci if (!n) 199262306a36Sopenharmony_ci continue; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci if (IS_TNODE(n)) { 199562306a36Sopenharmony_ci /* record pn and cindex for leaf walking */ 199662306a36Sopenharmony_ci pn = n; 199762306a36Sopenharmony_ci cindex = 1ul << n->bits; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci continue; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 200362306a36Sopenharmony_ci /* if alias was cloned to local then we just 200462306a36Sopenharmony_ci * need to remove the local copy from main 200562306a36Sopenharmony_ci */ 200662306a36Sopenharmony_ci if (tb->tb_id != fa->tb_id) { 200762306a36Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 200862306a36Sopenharmony_ci alias_free_mem_rcu(fa); 200962306a36Sopenharmony_ci continue; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* record local slen */ 201362306a36Sopenharmony_ci slen = fa->fa_slen; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* update leaf slen */ 201762306a36Sopenharmony_ci n->slen = slen; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci if (hlist_empty(&n->leaf)) { 202062306a36Sopenharmony_ci put_child_root(pn, n->key, NULL); 202162306a36Sopenharmony_ci node_free(n); 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci/* Caller must hold RTNL. */ 202762306a36Sopenharmony_ciint fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 203062306a36Sopenharmony_ci struct nl_info info = { .nl_net = net }; 203162306a36Sopenharmony_ci struct key_vector *pn = t->kv; 203262306a36Sopenharmony_ci unsigned long cindex = 1; 203362306a36Sopenharmony_ci struct hlist_node *tmp; 203462306a36Sopenharmony_ci struct fib_alias *fa; 203562306a36Sopenharmony_ci int found = 0; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* walk trie in reverse order */ 203862306a36Sopenharmony_ci for (;;) { 203962306a36Sopenharmony_ci unsigned char slen = 0; 204062306a36Sopenharmony_ci struct key_vector *n; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci if (!(cindex--)) { 204362306a36Sopenharmony_ci t_key pkey = pn->key; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci /* cannot resize the trie vector */ 204662306a36Sopenharmony_ci if (IS_TRIE(pn)) 204762306a36Sopenharmony_ci break; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci /* update the suffix to address pulled leaves */ 205062306a36Sopenharmony_ci if (pn->slen > pn->pos) 205162306a36Sopenharmony_ci update_suffix(pn); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci /* resize completed node */ 205462306a36Sopenharmony_ci pn = resize(t, pn); 205562306a36Sopenharmony_ci cindex = get_index(pkey, pn); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci continue; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* grab the next available node */ 206162306a36Sopenharmony_ci n = get_child(pn, cindex); 206262306a36Sopenharmony_ci if (!n) 206362306a36Sopenharmony_ci continue; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (IS_TNODE(n)) { 206662306a36Sopenharmony_ci /* record pn and cindex for leaf walking */ 206762306a36Sopenharmony_ci pn = n; 206862306a36Sopenharmony_ci cindex = 1ul << n->bits; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci continue; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { 207462306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci if (!fi || tb->tb_id != fa->tb_id || 207762306a36Sopenharmony_ci (!(fi->fib_flags & RTNH_F_DEAD) && 207862306a36Sopenharmony_ci !fib_props[fa->fa_type].error)) { 207962306a36Sopenharmony_ci slen = fa->fa_slen; 208062306a36Sopenharmony_ci continue; 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* Do not flush error routes if network namespace is 208462306a36Sopenharmony_ci * not being dismantled 208562306a36Sopenharmony_ci */ 208662306a36Sopenharmony_ci if (!flush_all && fib_props[fa->fa_type].error) { 208762306a36Sopenharmony_ci slen = fa->fa_slen; 208862306a36Sopenharmony_ci continue; 208962306a36Sopenharmony_ci } 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci fib_notify_alias_delete(net, n->key, &n->leaf, fa, 209262306a36Sopenharmony_ci NULL); 209362306a36Sopenharmony_ci if (fi->pfsrc_removed) 209462306a36Sopenharmony_ci rtmsg_fib(RTM_DELROUTE, htonl(n->key), fa, 209562306a36Sopenharmony_ci KEYLENGTH - fa->fa_slen, tb->tb_id, &info, 0); 209662306a36Sopenharmony_ci hlist_del_rcu(&fa->fa_list); 209762306a36Sopenharmony_ci fib_release_info(fa->fa_info); 209862306a36Sopenharmony_ci alias_free_mem_rcu(fa); 209962306a36Sopenharmony_ci found++; 210062306a36Sopenharmony_ci } 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* update leaf slen */ 210362306a36Sopenharmony_ci n->slen = slen; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (hlist_empty(&n->leaf)) { 210662306a36Sopenharmony_ci put_child_root(pn, n->key, NULL); 210762306a36Sopenharmony_ci node_free(n); 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci pr_debug("trie_flush found=%d\n", found); 211262306a36Sopenharmony_ci return found; 211362306a36Sopenharmony_ci} 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci/* derived from fib_trie_free */ 211662306a36Sopenharmony_cistatic void __fib_info_notify_update(struct net *net, struct fib_table *tb, 211762306a36Sopenharmony_ci struct nl_info *info) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 212062306a36Sopenharmony_ci struct key_vector *pn = t->kv; 212162306a36Sopenharmony_ci unsigned long cindex = 1; 212262306a36Sopenharmony_ci struct fib_alias *fa; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci for (;;) { 212562306a36Sopenharmony_ci struct key_vector *n; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (!(cindex--)) { 212862306a36Sopenharmony_ci t_key pkey = pn->key; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci if (IS_TRIE(pn)) 213162306a36Sopenharmony_ci break; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci pn = node_parent(pn); 213462306a36Sopenharmony_ci cindex = get_index(pkey, pn); 213562306a36Sopenharmony_ci continue; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci /* grab the next available node */ 213962306a36Sopenharmony_ci n = get_child(pn, cindex); 214062306a36Sopenharmony_ci if (!n) 214162306a36Sopenharmony_ci continue; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (IS_TNODE(n)) { 214462306a36Sopenharmony_ci /* record pn and cindex for leaf walking */ 214562306a36Sopenharmony_ci pn = n; 214662306a36Sopenharmony_ci cindex = 1ul << n->bits; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci continue; 214962306a36Sopenharmony_ci } 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci hlist_for_each_entry(fa, &n->leaf, fa_list) { 215262306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (!fi || !fi->nh_updated || fa->tb_id != tb->tb_id) 215562306a36Sopenharmony_ci continue; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci rtmsg_fib(RTM_NEWROUTE, htonl(n->key), fa, 215862306a36Sopenharmony_ci KEYLENGTH - fa->fa_slen, tb->tb_id, 215962306a36Sopenharmony_ci info, NLM_F_REPLACE); 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci} 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_civoid fib_info_notify_update(struct net *net, struct nl_info *info) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci unsigned int h; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 216962306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 217062306a36Sopenharmony_ci struct fib_table *tb; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist, 217362306a36Sopenharmony_ci lockdep_rtnl_is_held()) 217462306a36Sopenharmony_ci __fib_info_notify_update(net, tb, info); 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci} 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_cistatic int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, 217962306a36Sopenharmony_ci struct notifier_block *nb, 218062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 218162306a36Sopenharmony_ci{ 218262306a36Sopenharmony_ci struct fib_alias *fa; 218362306a36Sopenharmony_ci int last_slen = -1; 218462306a36Sopenharmony_ci int err; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 218762306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci if (!fi) 219062306a36Sopenharmony_ci continue; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci /* local and main table can share the same trie, 219362306a36Sopenharmony_ci * so don't notify twice for the same entry. 219462306a36Sopenharmony_ci */ 219562306a36Sopenharmony_ci if (tb->tb_id != fa->tb_id) 219662306a36Sopenharmony_ci continue; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci if (fa->fa_slen == last_slen) 219962306a36Sopenharmony_ci continue; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci last_slen = fa->fa_slen; 220262306a36Sopenharmony_ci err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE, 220362306a36Sopenharmony_ci l->key, KEYLENGTH - fa->fa_slen, 220462306a36Sopenharmony_ci fa, extack); 220562306a36Sopenharmony_ci if (err) 220662306a36Sopenharmony_ci return err; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci return 0; 220962306a36Sopenharmony_ci} 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_cistatic int fib_table_notify(struct fib_table *tb, struct notifier_block *nb, 221262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 221362306a36Sopenharmony_ci{ 221462306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 221562306a36Sopenharmony_ci struct key_vector *l, *tp = t->kv; 221662306a36Sopenharmony_ci t_key key = 0; 221762306a36Sopenharmony_ci int err; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 222062306a36Sopenharmony_ci err = fib_leaf_notify(l, tb, nb, extack); 222162306a36Sopenharmony_ci if (err) 222262306a36Sopenharmony_ci return err; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci key = l->key + 1; 222562306a36Sopenharmony_ci /* stop in case of wrap around */ 222662306a36Sopenharmony_ci if (key < l->key) 222762306a36Sopenharmony_ci break; 222862306a36Sopenharmony_ci } 222962306a36Sopenharmony_ci return 0; 223062306a36Sopenharmony_ci} 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ciint fib_notify(struct net *net, struct notifier_block *nb, 223362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 223462306a36Sopenharmony_ci{ 223562306a36Sopenharmony_ci unsigned int h; 223662306a36Sopenharmony_ci int err; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 223962306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 224062306a36Sopenharmony_ci struct fib_table *tb; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 224362306a36Sopenharmony_ci err = fib_table_notify(tb, nb, extack); 224462306a36Sopenharmony_ci if (err) 224562306a36Sopenharmony_ci return err; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci return 0; 224962306a36Sopenharmony_ci} 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_cistatic void __trie_free_rcu(struct rcu_head *head) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct fib_table *tb = container_of(head, struct fib_table, rcu); 225462306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 225562306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (tb->tb_data == tb->__data) 225862306a36Sopenharmony_ci free_percpu(t->stats); 225962306a36Sopenharmony_ci#endif /* CONFIG_IP_FIB_TRIE_STATS */ 226062306a36Sopenharmony_ci kfree(tb); 226162306a36Sopenharmony_ci} 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_civoid fib_free_table(struct fib_table *tb) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci call_rcu(&tb->rcu, __trie_free_rcu); 226662306a36Sopenharmony_ci} 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_cistatic int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb, 226962306a36Sopenharmony_ci struct sk_buff *skb, struct netlink_callback *cb, 227062306a36Sopenharmony_ci struct fib_dump_filter *filter) 227162306a36Sopenharmony_ci{ 227262306a36Sopenharmony_ci unsigned int flags = NLM_F_MULTI; 227362306a36Sopenharmony_ci __be32 xkey = htonl(l->key); 227462306a36Sopenharmony_ci int i, s_i, i_fa, s_fa, err; 227562306a36Sopenharmony_ci struct fib_alias *fa; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (filter->filter_set || 227862306a36Sopenharmony_ci !filter->dump_exceptions || !filter->dump_routes) 227962306a36Sopenharmony_ci flags |= NLM_F_DUMP_FILTERED; 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci s_i = cb->args[4]; 228262306a36Sopenharmony_ci s_fa = cb->args[5]; 228362306a36Sopenharmony_ci i = 0; 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci /* rcu_read_lock is hold by caller */ 228662306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 228762306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (i < s_i) 229062306a36Sopenharmony_ci goto next; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci i_fa = 0; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci if (tb->tb_id != fa->tb_id) 229562306a36Sopenharmony_ci goto next; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci if (filter->filter_set) { 229862306a36Sopenharmony_ci if (filter->rt_type && fa->fa_type != filter->rt_type) 229962306a36Sopenharmony_ci goto next; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci if ((filter->protocol && 230262306a36Sopenharmony_ci fi->fib_protocol != filter->protocol)) 230362306a36Sopenharmony_ci goto next; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci if (filter->dev && 230662306a36Sopenharmony_ci !fib_info_nh_uses_dev(fi, filter->dev)) 230762306a36Sopenharmony_ci goto next; 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci if (filter->dump_routes) { 231162306a36Sopenharmony_ci if (!s_fa) { 231262306a36Sopenharmony_ci struct fib_rt_info fri; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci fri.fi = fi; 231562306a36Sopenharmony_ci fri.tb_id = tb->tb_id; 231662306a36Sopenharmony_ci fri.dst = xkey; 231762306a36Sopenharmony_ci fri.dst_len = KEYLENGTH - fa->fa_slen; 231862306a36Sopenharmony_ci fri.dscp = fa->fa_dscp; 231962306a36Sopenharmony_ci fri.type = fa->fa_type; 232062306a36Sopenharmony_ci fri.offload = READ_ONCE(fa->offload); 232162306a36Sopenharmony_ci fri.trap = READ_ONCE(fa->trap); 232262306a36Sopenharmony_ci fri.offload_failed = READ_ONCE(fa->offload_failed); 232362306a36Sopenharmony_ci err = fib_dump_info(skb, 232462306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 232562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 232662306a36Sopenharmony_ci RTM_NEWROUTE, &fri, flags); 232762306a36Sopenharmony_ci if (err < 0) 232862306a36Sopenharmony_ci goto stop; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci i_fa++; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci if (filter->dump_exceptions) { 233562306a36Sopenharmony_ci err = fib_dump_info_fnhe(skb, cb, tb->tb_id, fi, 233662306a36Sopenharmony_ci &i_fa, s_fa, flags); 233762306a36Sopenharmony_ci if (err < 0) 233862306a36Sopenharmony_ci goto stop; 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_cinext: 234262306a36Sopenharmony_ci i++; 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci cb->args[4] = i; 234662306a36Sopenharmony_ci return skb->len; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cistop: 234962306a36Sopenharmony_ci cb->args[4] = i; 235062306a36Sopenharmony_ci cb->args[5] = i_fa; 235162306a36Sopenharmony_ci return err; 235262306a36Sopenharmony_ci} 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */ 235562306a36Sopenharmony_ciint fib_table_dump(struct fib_table *tb, struct sk_buff *skb, 235662306a36Sopenharmony_ci struct netlink_callback *cb, struct fib_dump_filter *filter) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci struct trie *t = (struct trie *)tb->tb_data; 235962306a36Sopenharmony_ci struct key_vector *l, *tp = t->kv; 236062306a36Sopenharmony_ci /* Dump starting at last key. 236162306a36Sopenharmony_ci * Note: 0.0.0.0/0 (ie default) is first key. 236262306a36Sopenharmony_ci */ 236362306a36Sopenharmony_ci int count = cb->args[2]; 236462306a36Sopenharmony_ci t_key key = cb->args[3]; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* First time here, count and key are both always 0. Count > 0 236762306a36Sopenharmony_ci * and key == 0 means the dump has wrapped around and we are done. 236862306a36Sopenharmony_ci */ 236962306a36Sopenharmony_ci if (count && !key) 237062306a36Sopenharmony_ci return skb->len; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci while ((l = leaf_walk_rcu(&tp, key)) != NULL) { 237362306a36Sopenharmony_ci int err; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci err = fn_trie_dump_leaf(l, tb, skb, cb, filter); 237662306a36Sopenharmony_ci if (err < 0) { 237762306a36Sopenharmony_ci cb->args[3] = key; 237862306a36Sopenharmony_ci cb->args[2] = count; 237962306a36Sopenharmony_ci return err; 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci ++count; 238362306a36Sopenharmony_ci key = l->key + 1; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci memset(&cb->args[4], 0, 238662306a36Sopenharmony_ci sizeof(cb->args) - 4*sizeof(cb->args[0])); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci /* stop loop if key wrapped back to 0 */ 238962306a36Sopenharmony_ci if (key < l->key) 239062306a36Sopenharmony_ci break; 239162306a36Sopenharmony_ci } 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci cb->args[3] = key; 239462306a36Sopenharmony_ci cb->args[2] = count; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci return skb->len; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_civoid __init fib_trie_init(void) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci fn_alias_kmem = kmem_cache_create("ip_fib_alias", 240262306a36Sopenharmony_ci sizeof(struct fib_alias), 240362306a36Sopenharmony_ci 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci trie_leaf_kmem = kmem_cache_create("ip_fib_trie", 240662306a36Sopenharmony_ci LEAF_SIZE, 240762306a36Sopenharmony_ci 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); 240862306a36Sopenharmony_ci} 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_cistruct fib_table *fib_trie_table(u32 id, struct fib_table *alias) 241162306a36Sopenharmony_ci{ 241262306a36Sopenharmony_ci struct fib_table *tb; 241362306a36Sopenharmony_ci struct trie *t; 241462306a36Sopenharmony_ci size_t sz = sizeof(*tb); 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci if (!alias) 241762306a36Sopenharmony_ci sz += sizeof(struct trie); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci tb = kzalloc(sz, GFP_KERNEL); 242062306a36Sopenharmony_ci if (!tb) 242162306a36Sopenharmony_ci return NULL; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci tb->tb_id = id; 242462306a36Sopenharmony_ci tb->tb_num_default = 0; 242562306a36Sopenharmony_ci tb->tb_data = (alias ? alias->__data : tb->__data); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (alias) 242862306a36Sopenharmony_ci return tb; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci t = (struct trie *) tb->tb_data; 243162306a36Sopenharmony_ci t->kv[0].pos = KEYLENGTH; 243262306a36Sopenharmony_ci t->kv[0].slen = KEYLENGTH; 243362306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 243462306a36Sopenharmony_ci t->stats = alloc_percpu(struct trie_use_stats); 243562306a36Sopenharmony_ci if (!t->stats) { 243662306a36Sopenharmony_ci kfree(tb); 243762306a36Sopenharmony_ci tb = NULL; 243862306a36Sopenharmony_ci } 243962306a36Sopenharmony_ci#endif 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci return tb; 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 244562306a36Sopenharmony_ci/* Depth first Trie walk iterator */ 244662306a36Sopenharmony_cistruct fib_trie_iter { 244762306a36Sopenharmony_ci struct seq_net_private p; 244862306a36Sopenharmony_ci struct fib_table *tb; 244962306a36Sopenharmony_ci struct key_vector *tnode; 245062306a36Sopenharmony_ci unsigned int index; 245162306a36Sopenharmony_ci unsigned int depth; 245262306a36Sopenharmony_ci}; 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_cistatic struct key_vector *fib_trie_get_next(struct fib_trie_iter *iter) 245562306a36Sopenharmony_ci{ 245662306a36Sopenharmony_ci unsigned long cindex = iter->index; 245762306a36Sopenharmony_ci struct key_vector *pn = iter->tnode; 245862306a36Sopenharmony_ci t_key pkey; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci pr_debug("get_next iter={node=%p index=%d depth=%d}\n", 246162306a36Sopenharmony_ci iter->tnode, iter->index, iter->depth); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci while (!IS_TRIE(pn)) { 246462306a36Sopenharmony_ci while (cindex < child_length(pn)) { 246562306a36Sopenharmony_ci struct key_vector *n = get_child_rcu(pn, cindex++); 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci if (!n) 246862306a36Sopenharmony_ci continue; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci if (IS_LEAF(n)) { 247162306a36Sopenharmony_ci iter->tnode = pn; 247262306a36Sopenharmony_ci iter->index = cindex; 247362306a36Sopenharmony_ci } else { 247462306a36Sopenharmony_ci /* push down one level */ 247562306a36Sopenharmony_ci iter->tnode = n; 247662306a36Sopenharmony_ci iter->index = 0; 247762306a36Sopenharmony_ci ++iter->depth; 247862306a36Sopenharmony_ci } 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci return n; 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci /* Current node exhausted, pop back up */ 248462306a36Sopenharmony_ci pkey = pn->key; 248562306a36Sopenharmony_ci pn = node_parent_rcu(pn); 248662306a36Sopenharmony_ci cindex = get_index(pkey, pn) + 1; 248762306a36Sopenharmony_ci --iter->depth; 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci /* record root node so further searches know we are done */ 249162306a36Sopenharmony_ci iter->tnode = pn; 249262306a36Sopenharmony_ci iter->index = 0; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci return NULL; 249562306a36Sopenharmony_ci} 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_cistatic struct key_vector *fib_trie_get_first(struct fib_trie_iter *iter, 249862306a36Sopenharmony_ci struct trie *t) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci struct key_vector *n, *pn; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci if (!t) 250362306a36Sopenharmony_ci return NULL; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci pn = t->kv; 250662306a36Sopenharmony_ci n = rcu_dereference(pn->tnode[0]); 250762306a36Sopenharmony_ci if (!n) 250862306a36Sopenharmony_ci return NULL; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci if (IS_TNODE(n)) { 251162306a36Sopenharmony_ci iter->tnode = n; 251262306a36Sopenharmony_ci iter->index = 0; 251362306a36Sopenharmony_ci iter->depth = 1; 251462306a36Sopenharmony_ci } else { 251562306a36Sopenharmony_ci iter->tnode = pn; 251662306a36Sopenharmony_ci iter->index = 0; 251762306a36Sopenharmony_ci iter->depth = 0; 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci return n; 252162306a36Sopenharmony_ci} 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_cistatic void trie_collect_stats(struct trie *t, struct trie_stat *s) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci struct key_vector *n; 252662306a36Sopenharmony_ci struct fib_trie_iter iter; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci memset(s, 0, sizeof(*s)); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci rcu_read_lock(); 253162306a36Sopenharmony_ci for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) { 253262306a36Sopenharmony_ci if (IS_LEAF(n)) { 253362306a36Sopenharmony_ci struct fib_alias *fa; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci s->leaves++; 253662306a36Sopenharmony_ci s->totdepth += iter.depth; 253762306a36Sopenharmony_ci if (iter.depth > s->maxdepth) 253862306a36Sopenharmony_ci s->maxdepth = iter.depth; 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) 254162306a36Sopenharmony_ci ++s->prefixes; 254262306a36Sopenharmony_ci } else { 254362306a36Sopenharmony_ci s->tnodes++; 254462306a36Sopenharmony_ci if (n->bits < MAX_STAT_DEPTH) 254562306a36Sopenharmony_ci s->nodesizes[n->bits]++; 254662306a36Sopenharmony_ci s->nullpointers += tn_info(n)->empty_children; 254762306a36Sopenharmony_ci } 254862306a36Sopenharmony_ci } 254962306a36Sopenharmony_ci rcu_read_unlock(); 255062306a36Sopenharmony_ci} 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci/* 255362306a36Sopenharmony_ci * This outputs /proc/net/fib_triestats 255462306a36Sopenharmony_ci */ 255562306a36Sopenharmony_cistatic void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) 255662306a36Sopenharmony_ci{ 255762306a36Sopenharmony_ci unsigned int i, max, pointers, bytes, avdepth; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci if (stat->leaves) 256062306a36Sopenharmony_ci avdepth = stat->totdepth*100 / stat->leaves; 256162306a36Sopenharmony_ci else 256262306a36Sopenharmony_ci avdepth = 0; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci seq_printf(seq, "\tAver depth: %u.%02d\n", 256562306a36Sopenharmony_ci avdepth / 100, avdepth % 100); 256662306a36Sopenharmony_ci seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth); 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci seq_printf(seq, "\tLeaves: %u\n", stat->leaves); 256962306a36Sopenharmony_ci bytes = LEAF_SIZE * stat->leaves; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci seq_printf(seq, "\tPrefixes: %u\n", stat->prefixes); 257262306a36Sopenharmony_ci bytes += sizeof(struct fib_alias) * stat->prefixes; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci seq_printf(seq, "\tInternal nodes: %u\n\t", stat->tnodes); 257562306a36Sopenharmony_ci bytes += TNODE_SIZE(0) * stat->tnodes; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci max = MAX_STAT_DEPTH; 257862306a36Sopenharmony_ci while (max > 0 && stat->nodesizes[max-1] == 0) 257962306a36Sopenharmony_ci max--; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci pointers = 0; 258262306a36Sopenharmony_ci for (i = 1; i < max; i++) 258362306a36Sopenharmony_ci if (stat->nodesizes[i] != 0) { 258462306a36Sopenharmony_ci seq_printf(seq, " %u: %u", i, stat->nodesizes[i]); 258562306a36Sopenharmony_ci pointers += (1<<i) * stat->nodesizes[i]; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci seq_putc(seq, '\n'); 258862306a36Sopenharmony_ci seq_printf(seq, "\tPointers: %u\n", pointers); 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci bytes += sizeof(struct key_vector *) * pointers; 259162306a36Sopenharmony_ci seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers); 259262306a36Sopenharmony_ci seq_printf(seq, "Total size: %u kB\n", (bytes + 1023) / 1024); 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 259662306a36Sopenharmony_cistatic void trie_show_usage(struct seq_file *seq, 259762306a36Sopenharmony_ci const struct trie_use_stats __percpu *stats) 259862306a36Sopenharmony_ci{ 259962306a36Sopenharmony_ci struct trie_use_stats s = { 0 }; 260062306a36Sopenharmony_ci int cpu; 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci /* loop through all of the CPUs and gather up the stats */ 260362306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 260462306a36Sopenharmony_ci const struct trie_use_stats *pcpu = per_cpu_ptr(stats, cpu); 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci s.gets += pcpu->gets; 260762306a36Sopenharmony_ci s.backtrack += pcpu->backtrack; 260862306a36Sopenharmony_ci s.semantic_match_passed += pcpu->semantic_match_passed; 260962306a36Sopenharmony_ci s.semantic_match_miss += pcpu->semantic_match_miss; 261062306a36Sopenharmony_ci s.null_node_hit += pcpu->null_node_hit; 261162306a36Sopenharmony_ci s.resize_node_skipped += pcpu->resize_node_skipped; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci seq_printf(seq, "\nCounters:\n---------\n"); 261562306a36Sopenharmony_ci seq_printf(seq, "gets = %u\n", s.gets); 261662306a36Sopenharmony_ci seq_printf(seq, "backtracks = %u\n", s.backtrack); 261762306a36Sopenharmony_ci seq_printf(seq, "semantic match passed = %u\n", 261862306a36Sopenharmony_ci s.semantic_match_passed); 261962306a36Sopenharmony_ci seq_printf(seq, "semantic match miss = %u\n", s.semantic_match_miss); 262062306a36Sopenharmony_ci seq_printf(seq, "null node hit= %u\n", s.null_node_hit); 262162306a36Sopenharmony_ci seq_printf(seq, "skipped node resize = %u\n\n", s.resize_node_skipped); 262262306a36Sopenharmony_ci} 262362306a36Sopenharmony_ci#endif /* CONFIG_IP_FIB_TRIE_STATS */ 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_cistatic void fib_table_print(struct seq_file *seq, struct fib_table *tb) 262662306a36Sopenharmony_ci{ 262762306a36Sopenharmony_ci if (tb->tb_id == RT_TABLE_LOCAL) 262862306a36Sopenharmony_ci seq_puts(seq, "Local:\n"); 262962306a36Sopenharmony_ci else if (tb->tb_id == RT_TABLE_MAIN) 263062306a36Sopenharmony_ci seq_puts(seq, "Main:\n"); 263162306a36Sopenharmony_ci else 263262306a36Sopenharmony_ci seq_printf(seq, "Id %d:\n", tb->tb_id); 263362306a36Sopenharmony_ci} 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_cistatic int fib_triestat_seq_show(struct seq_file *seq, void *v) 263762306a36Sopenharmony_ci{ 263862306a36Sopenharmony_ci struct net *net = seq->private; 263962306a36Sopenharmony_ci unsigned int h; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci seq_printf(seq, 264262306a36Sopenharmony_ci "Basic info: size of leaf:" 264362306a36Sopenharmony_ci " %zd bytes, size of tnode: %zd bytes.\n", 264462306a36Sopenharmony_ci LEAF_SIZE, TNODE_SIZE(0)); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci rcu_read_lock(); 264762306a36Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 264862306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 264962306a36Sopenharmony_ci struct fib_table *tb; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 265262306a36Sopenharmony_ci struct trie *t = (struct trie *) tb->tb_data; 265362306a36Sopenharmony_ci struct trie_stat stat; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (!t) 265662306a36Sopenharmony_ci continue; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci fib_table_print(seq, tb); 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci trie_collect_stats(t, &stat); 266162306a36Sopenharmony_ci trie_show_stats(seq, &stat); 266262306a36Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS 266362306a36Sopenharmony_ci trie_show_usage(seq, t->stats); 266462306a36Sopenharmony_ci#endif 266562306a36Sopenharmony_ci } 266662306a36Sopenharmony_ci cond_resched_rcu(); 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci rcu_read_unlock(); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci return 0; 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_cistatic struct key_vector *fib_trie_get_idx(struct seq_file *seq, loff_t pos) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci struct fib_trie_iter *iter = seq->private; 267662306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 267762306a36Sopenharmony_ci loff_t idx = 0; 267862306a36Sopenharmony_ci unsigned int h; 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 268162306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 268262306a36Sopenharmony_ci struct fib_table *tb; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 268562306a36Sopenharmony_ci struct key_vector *n; 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci for (n = fib_trie_get_first(iter, 268862306a36Sopenharmony_ci (struct trie *) tb->tb_data); 268962306a36Sopenharmony_ci n; n = fib_trie_get_next(iter)) 269062306a36Sopenharmony_ci if (pos == idx++) { 269162306a36Sopenharmony_ci iter->tb = tb; 269262306a36Sopenharmony_ci return n; 269362306a36Sopenharmony_ci } 269462306a36Sopenharmony_ci } 269562306a36Sopenharmony_ci } 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci return NULL; 269862306a36Sopenharmony_ci} 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_cistatic void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos) 270162306a36Sopenharmony_ci __acquires(RCU) 270262306a36Sopenharmony_ci{ 270362306a36Sopenharmony_ci rcu_read_lock(); 270462306a36Sopenharmony_ci return fib_trie_get_idx(seq, *pos); 270562306a36Sopenharmony_ci} 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_cistatic void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos) 270862306a36Sopenharmony_ci{ 270962306a36Sopenharmony_ci struct fib_trie_iter *iter = seq->private; 271062306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 271162306a36Sopenharmony_ci struct fib_table *tb = iter->tb; 271262306a36Sopenharmony_ci struct hlist_node *tb_node; 271362306a36Sopenharmony_ci unsigned int h; 271462306a36Sopenharmony_ci struct key_vector *n; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci ++*pos; 271762306a36Sopenharmony_ci /* next node in same table */ 271862306a36Sopenharmony_ci n = fib_trie_get_next(iter); 271962306a36Sopenharmony_ci if (n) 272062306a36Sopenharmony_ci return n; 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci /* walk rest of this hash chain */ 272362306a36Sopenharmony_ci h = tb->tb_id & (FIB_TABLE_HASHSZ - 1); 272462306a36Sopenharmony_ci while ((tb_node = rcu_dereference(hlist_next_rcu(&tb->tb_hlist)))) { 272562306a36Sopenharmony_ci tb = hlist_entry(tb_node, struct fib_table, tb_hlist); 272662306a36Sopenharmony_ci n = fib_trie_get_first(iter, (struct trie *) tb->tb_data); 272762306a36Sopenharmony_ci if (n) 272862306a36Sopenharmony_ci goto found; 272962306a36Sopenharmony_ci } 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci /* new hash chain */ 273262306a36Sopenharmony_ci while (++h < FIB_TABLE_HASHSZ) { 273362306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 273462306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 273562306a36Sopenharmony_ci n = fib_trie_get_first(iter, (struct trie *) tb->tb_data); 273662306a36Sopenharmony_ci if (n) 273762306a36Sopenharmony_ci goto found; 273862306a36Sopenharmony_ci } 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci return NULL; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_cifound: 274362306a36Sopenharmony_ci iter->tb = tb; 274462306a36Sopenharmony_ci return n; 274562306a36Sopenharmony_ci} 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_cistatic void fib_trie_seq_stop(struct seq_file *seq, void *v) 274862306a36Sopenharmony_ci __releases(RCU) 274962306a36Sopenharmony_ci{ 275062306a36Sopenharmony_ci rcu_read_unlock(); 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_cistatic void seq_indent(struct seq_file *seq, int n) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci while (n-- > 0) 275662306a36Sopenharmony_ci seq_puts(seq, " "); 275762306a36Sopenharmony_ci} 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_cistatic inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s) 276062306a36Sopenharmony_ci{ 276162306a36Sopenharmony_ci switch (s) { 276262306a36Sopenharmony_ci case RT_SCOPE_UNIVERSE: return "universe"; 276362306a36Sopenharmony_ci case RT_SCOPE_SITE: return "site"; 276462306a36Sopenharmony_ci case RT_SCOPE_LINK: return "link"; 276562306a36Sopenharmony_ci case RT_SCOPE_HOST: return "host"; 276662306a36Sopenharmony_ci case RT_SCOPE_NOWHERE: return "nowhere"; 276762306a36Sopenharmony_ci default: 276862306a36Sopenharmony_ci snprintf(buf, len, "scope=%d", s); 276962306a36Sopenharmony_ci return buf; 277062306a36Sopenharmony_ci } 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic const char *const rtn_type_names[__RTN_MAX] = { 277462306a36Sopenharmony_ci [RTN_UNSPEC] = "UNSPEC", 277562306a36Sopenharmony_ci [RTN_UNICAST] = "UNICAST", 277662306a36Sopenharmony_ci [RTN_LOCAL] = "LOCAL", 277762306a36Sopenharmony_ci [RTN_BROADCAST] = "BROADCAST", 277862306a36Sopenharmony_ci [RTN_ANYCAST] = "ANYCAST", 277962306a36Sopenharmony_ci [RTN_MULTICAST] = "MULTICAST", 278062306a36Sopenharmony_ci [RTN_BLACKHOLE] = "BLACKHOLE", 278162306a36Sopenharmony_ci [RTN_UNREACHABLE] = "UNREACHABLE", 278262306a36Sopenharmony_ci [RTN_PROHIBIT] = "PROHIBIT", 278362306a36Sopenharmony_ci [RTN_THROW] = "THROW", 278462306a36Sopenharmony_ci [RTN_NAT] = "NAT", 278562306a36Sopenharmony_ci [RTN_XRESOLVE] = "XRESOLVE", 278662306a36Sopenharmony_ci}; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_cistatic inline const char *rtn_type(char *buf, size_t len, unsigned int t) 278962306a36Sopenharmony_ci{ 279062306a36Sopenharmony_ci if (t < __RTN_MAX && rtn_type_names[t]) 279162306a36Sopenharmony_ci return rtn_type_names[t]; 279262306a36Sopenharmony_ci snprintf(buf, len, "type %u", t); 279362306a36Sopenharmony_ci return buf; 279462306a36Sopenharmony_ci} 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci/* Pretty print the trie */ 279762306a36Sopenharmony_cistatic int fib_trie_seq_show(struct seq_file *seq, void *v) 279862306a36Sopenharmony_ci{ 279962306a36Sopenharmony_ci const struct fib_trie_iter *iter = seq->private; 280062306a36Sopenharmony_ci struct key_vector *n = v; 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci if (IS_TRIE(node_parent_rcu(n))) 280362306a36Sopenharmony_ci fib_table_print(seq, iter->tb); 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci if (IS_TNODE(n)) { 280662306a36Sopenharmony_ci __be32 prf = htonl(n->key); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci seq_indent(seq, iter->depth-1); 280962306a36Sopenharmony_ci seq_printf(seq, " +-- %pI4/%zu %u %u %u\n", 281062306a36Sopenharmony_ci &prf, KEYLENGTH - n->pos - n->bits, n->bits, 281162306a36Sopenharmony_ci tn_info(n)->full_children, 281262306a36Sopenharmony_ci tn_info(n)->empty_children); 281362306a36Sopenharmony_ci } else { 281462306a36Sopenharmony_ci __be32 val = htonl(n->key); 281562306a36Sopenharmony_ci struct fib_alias *fa; 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci seq_indent(seq, iter->depth); 281862306a36Sopenharmony_ci seq_printf(seq, " |-- %pI4\n", &val); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) { 282162306a36Sopenharmony_ci char buf1[32], buf2[32]; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci seq_indent(seq, iter->depth + 1); 282462306a36Sopenharmony_ci seq_printf(seq, " /%zu %s %s", 282562306a36Sopenharmony_ci KEYLENGTH - fa->fa_slen, 282662306a36Sopenharmony_ci rtn_scope(buf1, sizeof(buf1), 282762306a36Sopenharmony_ci fa->fa_info->fib_scope), 282862306a36Sopenharmony_ci rtn_type(buf2, sizeof(buf2), 282962306a36Sopenharmony_ci fa->fa_type)); 283062306a36Sopenharmony_ci if (fa->fa_dscp) 283162306a36Sopenharmony_ci seq_printf(seq, " tos=%d", 283262306a36Sopenharmony_ci inet_dscp_to_dsfield(fa->fa_dscp)); 283362306a36Sopenharmony_ci seq_putc(seq, '\n'); 283462306a36Sopenharmony_ci } 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci return 0; 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_cistatic const struct seq_operations fib_trie_seq_ops = { 284162306a36Sopenharmony_ci .start = fib_trie_seq_start, 284262306a36Sopenharmony_ci .next = fib_trie_seq_next, 284362306a36Sopenharmony_ci .stop = fib_trie_seq_stop, 284462306a36Sopenharmony_ci .show = fib_trie_seq_show, 284562306a36Sopenharmony_ci}; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_cistruct fib_route_iter { 284862306a36Sopenharmony_ci struct seq_net_private p; 284962306a36Sopenharmony_ci struct fib_table *main_tb; 285062306a36Sopenharmony_ci struct key_vector *tnode; 285162306a36Sopenharmony_ci loff_t pos; 285262306a36Sopenharmony_ci t_key key; 285362306a36Sopenharmony_ci}; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cistatic struct key_vector *fib_route_get_idx(struct fib_route_iter *iter, 285662306a36Sopenharmony_ci loff_t pos) 285762306a36Sopenharmony_ci{ 285862306a36Sopenharmony_ci struct key_vector *l, **tp = &iter->tnode; 285962306a36Sopenharmony_ci t_key key; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci /* use cached location of previously found key */ 286262306a36Sopenharmony_ci if (iter->pos > 0 && pos >= iter->pos) { 286362306a36Sopenharmony_ci key = iter->key; 286462306a36Sopenharmony_ci } else { 286562306a36Sopenharmony_ci iter->pos = 1; 286662306a36Sopenharmony_ci key = 0; 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci pos -= iter->pos; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci while ((l = leaf_walk_rcu(tp, key)) && (pos-- > 0)) { 287262306a36Sopenharmony_ci key = l->key + 1; 287362306a36Sopenharmony_ci iter->pos++; 287462306a36Sopenharmony_ci l = NULL; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci /* handle unlikely case of a key wrap */ 287762306a36Sopenharmony_ci if (!key) 287862306a36Sopenharmony_ci break; 287962306a36Sopenharmony_ci } 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci if (l) 288262306a36Sopenharmony_ci iter->key = l->key; /* remember it */ 288362306a36Sopenharmony_ci else 288462306a36Sopenharmony_ci iter->pos = 0; /* forget it */ 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci return l; 288762306a36Sopenharmony_ci} 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_cistatic void *fib_route_seq_start(struct seq_file *seq, loff_t *pos) 289062306a36Sopenharmony_ci __acquires(RCU) 289162306a36Sopenharmony_ci{ 289262306a36Sopenharmony_ci struct fib_route_iter *iter = seq->private; 289362306a36Sopenharmony_ci struct fib_table *tb; 289462306a36Sopenharmony_ci struct trie *t; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci rcu_read_lock(); 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci tb = fib_get_table(seq_file_net(seq), RT_TABLE_MAIN); 289962306a36Sopenharmony_ci if (!tb) 290062306a36Sopenharmony_ci return NULL; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci iter->main_tb = tb; 290362306a36Sopenharmony_ci t = (struct trie *)tb->tb_data; 290462306a36Sopenharmony_ci iter->tnode = t->kv; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci if (*pos != 0) 290762306a36Sopenharmony_ci return fib_route_get_idx(iter, *pos); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci iter->pos = 0; 291062306a36Sopenharmony_ci iter->key = KEY_MAX; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci return SEQ_START_TOKEN; 291362306a36Sopenharmony_ci} 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_cistatic void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) 291662306a36Sopenharmony_ci{ 291762306a36Sopenharmony_ci struct fib_route_iter *iter = seq->private; 291862306a36Sopenharmony_ci struct key_vector *l = NULL; 291962306a36Sopenharmony_ci t_key key = iter->key + 1; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci ++*pos; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci /* only allow key of 0 for start of sequence */ 292462306a36Sopenharmony_ci if ((v == SEQ_START_TOKEN) || key) 292562306a36Sopenharmony_ci l = leaf_walk_rcu(&iter->tnode, key); 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (l) { 292862306a36Sopenharmony_ci iter->key = l->key; 292962306a36Sopenharmony_ci iter->pos++; 293062306a36Sopenharmony_ci } else { 293162306a36Sopenharmony_ci iter->pos = 0; 293262306a36Sopenharmony_ci } 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci return l; 293562306a36Sopenharmony_ci} 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_cistatic void fib_route_seq_stop(struct seq_file *seq, void *v) 293862306a36Sopenharmony_ci __releases(RCU) 293962306a36Sopenharmony_ci{ 294062306a36Sopenharmony_ci rcu_read_unlock(); 294162306a36Sopenharmony_ci} 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_cistatic unsigned int fib_flag_trans(int type, __be32 mask, struct fib_info *fi) 294462306a36Sopenharmony_ci{ 294562306a36Sopenharmony_ci unsigned int flags = 0; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (type == RTN_UNREACHABLE || type == RTN_PROHIBIT) 294862306a36Sopenharmony_ci flags = RTF_REJECT; 294962306a36Sopenharmony_ci if (fi) { 295062306a36Sopenharmony_ci const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci if (nhc->nhc_gw.ipv4) 295362306a36Sopenharmony_ci flags |= RTF_GATEWAY; 295462306a36Sopenharmony_ci } 295562306a36Sopenharmony_ci if (mask == htonl(0xFFFFFFFF)) 295662306a36Sopenharmony_ci flags |= RTF_HOST; 295762306a36Sopenharmony_ci flags |= RTF_UP; 295862306a36Sopenharmony_ci return flags; 295962306a36Sopenharmony_ci} 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci/* 296262306a36Sopenharmony_ci * This outputs /proc/net/route. 296362306a36Sopenharmony_ci * The format of the file is not supposed to be changed 296462306a36Sopenharmony_ci * and needs to be same as fib_hash output to avoid breaking 296562306a36Sopenharmony_ci * legacy utilities 296662306a36Sopenharmony_ci */ 296762306a36Sopenharmony_cistatic int fib_route_seq_show(struct seq_file *seq, void *v) 296862306a36Sopenharmony_ci{ 296962306a36Sopenharmony_ci struct fib_route_iter *iter = seq->private; 297062306a36Sopenharmony_ci struct fib_table *tb = iter->main_tb; 297162306a36Sopenharmony_ci struct fib_alias *fa; 297262306a36Sopenharmony_ci struct key_vector *l = v; 297362306a36Sopenharmony_ci __be32 prefix; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 297662306a36Sopenharmony_ci seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway " 297762306a36Sopenharmony_ci "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" 297862306a36Sopenharmony_ci "\tWindow\tIRTT"); 297962306a36Sopenharmony_ci return 0; 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci prefix = htonl(l->key); 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { 298562306a36Sopenharmony_ci struct fib_info *fi = fa->fa_info; 298662306a36Sopenharmony_ci __be32 mask = inet_make_mask(KEYLENGTH - fa->fa_slen); 298762306a36Sopenharmony_ci unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi); 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci if ((fa->fa_type == RTN_BROADCAST) || 299062306a36Sopenharmony_ci (fa->fa_type == RTN_MULTICAST)) 299162306a36Sopenharmony_ci continue; 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci if (fa->tb_id != tb->tb_id) 299462306a36Sopenharmony_ci continue; 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci seq_setwidth(seq, 127); 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci if (fi) { 299962306a36Sopenharmony_ci struct fib_nh_common *nhc = fib_info_nhc(fi, 0); 300062306a36Sopenharmony_ci __be32 gw = 0; 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci if (nhc->nhc_gw_family == AF_INET) 300362306a36Sopenharmony_ci gw = nhc->nhc_gw.ipv4; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci seq_printf(seq, 300662306a36Sopenharmony_ci "%s\t%08X\t%08X\t%04X\t%d\t%u\t" 300762306a36Sopenharmony_ci "%d\t%08X\t%d\t%u\t%u", 300862306a36Sopenharmony_ci nhc->nhc_dev ? nhc->nhc_dev->name : "*", 300962306a36Sopenharmony_ci prefix, gw, flags, 0, 0, 301062306a36Sopenharmony_ci fi->fib_priority, 301162306a36Sopenharmony_ci mask, 301262306a36Sopenharmony_ci (fi->fib_advmss ? 301362306a36Sopenharmony_ci fi->fib_advmss + 40 : 0), 301462306a36Sopenharmony_ci fi->fib_window, 301562306a36Sopenharmony_ci fi->fib_rtt >> 3); 301662306a36Sopenharmony_ci } else { 301762306a36Sopenharmony_ci seq_printf(seq, 301862306a36Sopenharmony_ci "*\t%08X\t%08X\t%04X\t%d\t%u\t" 301962306a36Sopenharmony_ci "%d\t%08X\t%d\t%u\t%u", 302062306a36Sopenharmony_ci prefix, 0, flags, 0, 0, 0, 302162306a36Sopenharmony_ci mask, 0, 0, 0); 302262306a36Sopenharmony_ci } 302362306a36Sopenharmony_ci seq_pad(seq, '\n'); 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci return 0; 302762306a36Sopenharmony_ci} 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_cistatic const struct seq_operations fib_route_seq_ops = { 303062306a36Sopenharmony_ci .start = fib_route_seq_start, 303162306a36Sopenharmony_ci .next = fib_route_seq_next, 303262306a36Sopenharmony_ci .stop = fib_route_seq_stop, 303362306a36Sopenharmony_ci .show = fib_route_seq_show, 303462306a36Sopenharmony_ci}; 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ciint __net_init fib_proc_init(struct net *net) 303762306a36Sopenharmony_ci{ 303862306a36Sopenharmony_ci if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, 303962306a36Sopenharmony_ci sizeof(struct fib_trie_iter))) 304062306a36Sopenharmony_ci goto out1; 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci if (!proc_create_net_single("fib_triestat", 0444, net->proc_net, 304362306a36Sopenharmony_ci fib_triestat_seq_show, NULL)) 304462306a36Sopenharmony_ci goto out2; 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci if (!proc_create_net("route", 0444, net->proc_net, &fib_route_seq_ops, 304762306a36Sopenharmony_ci sizeof(struct fib_route_iter))) 304862306a36Sopenharmony_ci goto out3; 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci return 0; 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ciout3: 305362306a36Sopenharmony_ci remove_proc_entry("fib_triestat", net->proc_net); 305462306a36Sopenharmony_ciout2: 305562306a36Sopenharmony_ci remove_proc_entry("fib_trie", net->proc_net); 305662306a36Sopenharmony_ciout1: 305762306a36Sopenharmony_ci return -ENOMEM; 305862306a36Sopenharmony_ci} 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_civoid __net_exit fib_proc_exit(struct net *net) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci remove_proc_entry("fib_trie", net->proc_net); 306362306a36Sopenharmony_ci remove_proc_entry("fib_triestat", net->proc_net); 306462306a36Sopenharmony_ci remove_proc_entry("route", net->proc_net); 306562306a36Sopenharmony_ci} 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 3068