diff options
Diffstat (limited to 'net/ipv4/fib_trie.c')
| -rw-r--r-- | net/ipv4/fib_trie.c | 176 | 
1 files changed, 67 insertions, 109 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index e2ffc2a5c7db..31cef3602585 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -73,6 +73,7 @@  #include <linux/slab.h>  #include <linux/export.h>  #include <linux/vmalloc.h> +#include <linux/notifier.h>  #include <net/net_namespace.h>  #include <net/ip.h>  #include <net/protocol.h> @@ -80,10 +81,47 @@  #include <net/tcp.h>  #include <net/sock.h>  #include <net/ip_fib.h> -#include <net/switchdev.h>  #include <trace/events/fib.h>  #include "fib_lookup.h" +static BLOCKING_NOTIFIER_HEAD(fib_chain); + +int register_fib_notifier(struct notifier_block *nb) +{ +	return blocking_notifier_chain_register(&fib_chain, nb); +} +EXPORT_SYMBOL(register_fib_notifier); + +int unregister_fib_notifier(struct notifier_block *nb) +{ +	return blocking_notifier_chain_unregister(&fib_chain, nb); +} +EXPORT_SYMBOL(unregister_fib_notifier); + +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, +		       struct fib_notifier_info *info) +{ +	info->net = net; +	return blocking_notifier_call_chain(&fib_chain, event_type, info); +} + +static int call_fib_entry_notifiers(struct net *net, +				    enum fib_event_type event_type, u32 dst, +				    int dst_len, struct fib_info *fi, +				    u8 tos, u8 type, u32 tb_id, u32 nlflags) +{ +	struct fib_entry_notifier_info info = { +		.dst = dst, +		.dst_len = dst_len, +		.fi = fi, +		.tos = tos, +		.type = type, +		.tb_id = tb_id, +		.nlflags = nlflags, +	}; +	return call_fib_notifiers(net, event_type, &info.info); +} +  #define MAX_STAT_DEPTH 32  #define KEYLENGTH	(8*sizeof(t_key)) @@ -1076,12 +1114,13 @@ static int fib_insert_alias(struct trie *t, struct key_vector *tp,  }  /* Caller must hold RTNL. */ -int fib_table_insert(struct fib_table *tb, struct fib_config *cfg) +int fib_table_insert(struct net *net, struct fib_table *tb, +		     struct fib_config *cfg)  {  	struct trie *t = (struct trie *)tb->tb_data;  	struct fib_alias *fa, *new_fa;  	struct key_vector *l, *tp; -	unsigned int nlflags = 0; +	u16 nlflags = NLM_F_EXCL;  	struct fib_info *fi;  	u8 plen = cfg->fc_dst_len;  	u8 slen = KEYLENGTH - plen; @@ -1126,6 +1165,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  		if (cfg->fc_nlflags & NLM_F_EXCL)  			goto out; +		nlflags &= ~NLM_F_EXCL; +  		/* We have 2 goals:  		 * 1. Find exact match for type, scope, fib_info to avoid  		 * duplicate routes @@ -1151,6 +1192,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  			struct fib_info *fi_drop;  			u8 state; +			nlflags |= NLM_F_REPLACE;  			fa = fa_first;  			if (fa_match) {  				if (fa == fa_match) @@ -1172,17 +1214,6 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  			new_fa->tb_id = tb->tb_id;  			new_fa->fa_default = -1; -			err = switchdev_fib_ipv4_add(key, plen, fi, -						     new_fa->fa_tos, -						     cfg->fc_type, -						     cfg->fc_nlflags, -						     tb->tb_id); -			if (err) { -				switchdev_fib_ipv4_abort(fi); -				kmem_cache_free(fn_alias_kmem, new_fa); -				goto out; -			} -  			hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);  			alias_free_mem_rcu(fa); @@ -1190,8 +1221,13 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  			fib_release_info(fi_drop);  			if (state & FA_S_ACCESSED)  				rt_cache_flush(cfg->fc_nlinfo.nl_net); + +			call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, +						 key, plen, fi, +						 new_fa->fa_tos, cfg->fc_type, +						 tb->tb_id, cfg->fc_nlflags);  			rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, -				tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE); +				tb->tb_id, &cfg->fc_nlinfo, nlflags);  			goto succeeded;  		} @@ -1203,7 +1239,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  			goto out;  		if (cfg->fc_nlflags & NLM_F_APPEND) -			nlflags = NLM_F_APPEND; +			nlflags |= NLM_F_APPEND;  		else  			fa = fa_first;  	} @@ -1211,6 +1247,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  	if (!(cfg->fc_nlflags & NLM_F_CREATE))  		goto out; +	nlflags |= NLM_F_CREATE;  	err = -ENOBUFS;  	new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);  	if (!new_fa) @@ -1224,30 +1261,22 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)  	new_fa->tb_id = tb->tb_id;  	new_fa->fa_default = -1; -	/* (Optionally) offload fib entry to switch hardware. */ -	err = switchdev_fib_ipv4_add(key, plen, fi, tos, cfg->fc_type, -				     cfg->fc_nlflags, tb->tb_id); -	if (err) { -		switchdev_fib_ipv4_abort(fi); -		goto out_free_new_fa; -	} -  	/* Insert new entry to the list. */  	err = fib_insert_alias(t, tp, l, new_fa, fa, key);  	if (err) -		goto out_sw_fib_del; +		goto out_free_new_fa;  	if (!plen)  		tb->tb_num_default++;  	rt_cache_flush(cfg->fc_nlinfo.nl_net); +	call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, key, plen, fi, tos, +				 cfg->fc_type, tb->tb_id, cfg->fc_nlflags);  	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,  		  &cfg->fc_nlinfo, nlflags);  succeeded:  	return 0; -out_sw_fib_del: -	switchdev_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id);  out_free_new_fa:  	kmem_cache_free(fn_alias_kmem, new_fa);  out: @@ -1486,7 +1515,8 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp,  }  /* Caller must hold RTNL. */ -int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) +int fib_table_delete(struct net *net, struct fib_table *tb, +		     struct fib_config *cfg)  {  	struct trie *t = (struct trie *) tb->tb_data;  	struct fib_alias *fa, *fa_to_delete; @@ -1539,9 +1569,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)  	if (!fa_to_delete)  		return -ESRCH; -	switchdev_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos, -			       cfg->fc_type, tb->tb_id); - +	call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen, +				 fa_to_delete->fa_info, tos, cfg->fc_type, +				 tb->tb_id, 0);  	rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,  		  &cfg->fc_nlinfo, 0); @@ -1730,82 +1760,8 @@ out:  	return NULL;  } -/* Caller must hold RTNL */ -void fib_table_flush_external(struct fib_table *tb) -{ -	struct trie *t = (struct trie *)tb->tb_data; -	struct key_vector *pn = t->kv; -	unsigned long cindex = 1; -	struct hlist_node *tmp; -	struct fib_alias *fa; - -	/* walk trie in reverse order */ -	for (;;) { -		unsigned char slen = 0; -		struct key_vector *n; - -		if (!(cindex--)) { -			t_key pkey = pn->key; - -			/* cannot resize the trie vector */ -			if (IS_TRIE(pn)) -				break; - -			/* resize completed node */ -			pn = resize(t, pn); -			cindex = get_index(pkey, pn); - -			continue; -		} - -		/* grab the next available node */ -		n = get_child(pn, cindex); -		if (!n) -			continue; - -		if (IS_TNODE(n)) { -			/* record pn and cindex for leaf walking */ -			pn = n; -			cindex = 1ul << n->bits; - -			continue; -		} - -		hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { -			struct fib_info *fi = fa->fa_info; - -			/* if alias was cloned to local then we just -			 * need to remove the local copy from main -			 */ -			if (tb->tb_id != fa->tb_id) { -				hlist_del_rcu(&fa->fa_list); -				alias_free_mem_rcu(fa); -				continue; -			} - -			/* record local slen */ -			slen = fa->fa_slen; - -			if (!fi || !(fi->fib_flags & RTNH_F_OFFLOAD)) -				continue; - -			switchdev_fib_ipv4_del(n->key, KEYLENGTH - fa->fa_slen, -					       fi, fa->fa_tos, fa->fa_type, -					       tb->tb_id); -		} - -		/* update leaf slen */ -		n->slen = slen; - -		if (hlist_empty(&n->leaf)) { -			put_child_root(pn, n->key, NULL); -			node_free(n); -		} -	} -} -  /* Caller must hold RTNL. */ -int fib_table_flush(struct fib_table *tb) +int fib_table_flush(struct net *net, struct fib_table *tb)  {  	struct trie *t = (struct trie *)tb->tb_data;  	struct key_vector *pn = t->kv; @@ -1854,9 +1810,11 @@ int fib_table_flush(struct fib_table *tb)  				continue;  			} -			switchdev_fib_ipv4_del(n->key, KEYLENGTH - fa->fa_slen, -					       fi, fa->fa_tos, fa->fa_type, -					       tb->tb_id); +			call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, +						 n->key, +						 KEYLENGTH - fa->fa_slen, +						 fi, fa->fa_tos, fa->fa_type, +						 tb->tb_id, 0);  			hlist_del_rcu(&fa->fa_list);  			fib_release_info(fa->fa_info);  			alias_free_mem_rcu(fa);  |