@@ -367,6 +367,7 @@ enum nft_chain_flags {
*
* @rules: list of rules in the chain
* @list: used internally
+ * @rcu_head: used internally
* @handle: chain handle
* @flags: bitmask of enum nft_chain_flags
* @use: number of jump references to this chain
@@ -376,6 +377,7 @@ enum nft_chain_flags {
struct nft_chain {
struct list_head rules;
struct list_head list;
+ struct rcu_head rcu_head;
u64 handle;
u8 flags;
u16 use;
@@ -882,6 +882,15 @@ notify:
return 0;
}
+static void nf_tables_rcu_chain_destroy(struct rcu_head *head)
+{
+ struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
+
+ BUG_ON(chain->use > 0);
+
+ kfree(chain);
+}
+
static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
@@ -905,7 +914,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(chain))
return PTR_ERR(chain);
- if (!list_empty(&chain->rules) || chain->use > 0)
+ if (!list_empty(&chain->rules))
return -EBUSY;
list_del(&chain->list);
@@ -917,7 +926,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
family);
- kfree(chain);
+ /* Make sure all rule references are gone before this is released */
+ call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy);
return 0;
}