[PATCH v1 net-next 03/10] ipv4: fib: Protect fib_new_table() with spinlock.
From: Kuniyuki Iwashima <kuniyu@google.com>
Date: 2026-06-29 18:12:32
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
fib_newrule() will drop RTNL except for the first IPv4 rule. Then, fib4_rule_configure() could call fib_empty_table() and create a new IPv4 fib_table without RTNL. Currently, net->ipv4.fib_table_hash[] is only protected by RTNL. As a prep, let's protect net->ipv4.fib_table_hash[] with a dedicated spinlock. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> --- include/net/netns/ipv4.h | 1 + net/ipv4/fib_frontend.c | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 6e27c56514df..59506320558a 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h@@ -127,6 +127,7 @@ struct netns_ipv4 { atomic_t fib_num_tclassid_users; #endif struct hlist_head *fib_table_hash; + spinlock_t fib_table_hash_lock; struct sock *fibnl; struct hlist_head *fib_info_hash; unsigned int fib_info_hash_bits;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 42212970d735..336d70649eb9 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c@@ -76,7 +76,7 @@ static int __net_init fib4_rules_init(struct net *net) struct fib_table *fib_new_table(struct net *net, u32 id) { - struct fib_table *tb, *alias = NULL; + struct fib_table *tb, *new_tb, *alias = NULL; unsigned int h; if (id == 0)
@@ -85,14 +85,27 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (tb) return tb; + if (!check_net(net)) + return NULL; + if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules) alias = fib_new_table(net, RT_TABLE_MAIN); - if (check_net(net)) - tb = fib_trie_table(id, alias); - if (!tb) + new_tb = fib_trie_table(id, alias); + if (!new_tb) return NULL; + spin_lock(&net->ipv4.fib_table_hash_lock); + + tb = fib_get_table(net, id); + if (tb) { + spin_unlock(&net->ipv4.fib_table_hash_lock); + fib_free_table(new_tb); + return tb; + } + + tb = new_tb; + switch (id) { case RT_TABLE_MAIN: rcu_assign_pointer(net->ipv4.fib_main, tb);
@@ -106,6 +119,9 @@ struct fib_table *fib_new_table(struct net *net, u32 id) h = id & (FIB_TABLE_HASHSZ - 1); hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); + + spin_unlock(&net->ipv4.fib_table_hash_lock); + return tb; } EXPORT_SYMBOL_GPL(fib_new_table);
@@ -1565,6 +1581,7 @@ static int __net_init ip_fib_net_init(struct net *net) net->ipv4.sysctl_fib_multipath_hash_fields = FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK; #endif + spin_lock_init(&net->ipv4.fib_table_hash_lock); /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES);
--
2.55.0.rc0.799.gd6f94ed593-goog