netfilter: cttimeout: decouple unlink and free on netns destruction
authorFlorian Westphal <fw@strlen.de>
Mon, 11 Apr 2022 11:01:19 +0000 (13:01 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 13 May 2022 16:52:16 +0000 (18:52 +0200)
Make it so netns pre_exit unlinks the objects from the pernet list, so
they cannot be found anymore.

netns core issues a synchronize_rcu() before calling the exit hooks so
any the time the exit hooks run unconfirmed nf_conn entries have been
free'd or they have been committed to the hashtable.

The exit hook still tags unconfirmed entries as dying, this can
now be removed in a followup change.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_conntrack_timeout.h
net/netfilter/nfnetlink_cttimeout.c

index 3ea94f6..fea2589 100644 (file)
@@ -17,14 +17,6 @@ struct nf_ct_timeout {
        char                    data[];
 };
 
-struct ctnl_timeout {
-       struct list_head        head;
-       struct rcu_head         rcu_head;
-       refcount_t              refcnt;
-       char                    name[CTNL_TIMEOUT_NAME_MAX];
-       struct nf_ct_timeout    timeout;
-};
-
 struct nf_conn_timeout {
        struct nf_ct_timeout __rcu *timeout;
 };
index eea486f..83fa15c 100644 (file)
 
 static unsigned int nfct_timeout_id __read_mostly;
 
+struct ctnl_timeout {
+       struct list_head        head;
+       struct rcu_head         rcu_head;
+       refcount_t              refcnt;
+       char                    name[CTNL_TIMEOUT_NAME_MAX];
+       struct nf_ct_timeout    timeout;
+
+       struct list_head        free_head;
+};
+
 struct nfct_timeout_pernet {
        struct list_head        nfct_timeout_list;
+       struct list_head        nfct_timeout_freelist;
 };
 
 MODULE_LICENSE("GPL");
@@ -574,10 +585,24 @@ static int __net_init cttimeout_net_init(struct net *net)
        struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
 
        INIT_LIST_HEAD(&pernet->nfct_timeout_list);
+       INIT_LIST_HEAD(&pernet->nfct_timeout_freelist);
 
        return 0;
 }
 
+static void __net_exit cttimeout_net_pre_exit(struct net *net)
+{
+       struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
+       struct ctnl_timeout *cur, *tmp;
+
+       list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) {
+               list_del_rcu(&cur->head);
+               list_add(&cur->free_head, &pernet->nfct_timeout_freelist);
+       }
+
+       /* core calls synchronize_rcu() after this */
+}
+
 static void __net_exit cttimeout_net_exit(struct net *net)
 {
        struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
@@ -586,8 +611,8 @@ static void __net_exit cttimeout_net_exit(struct net *net)
        nf_ct_unconfirmed_destroy(net);
        nf_ct_untimeout(net, NULL);
 
-       list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) {
-               list_del_rcu(&cur->head);
+       list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, head) {
+               list_del(&cur->free_head);
 
                if (refcount_dec_and_test(&cur->refcnt))
                        kfree_rcu(cur, rcu_head);
@@ -596,6 +621,7 @@ static void __net_exit cttimeout_net_exit(struct net *net)
 
 static struct pernet_operations cttimeout_ops = {
        .init   = cttimeout_net_init,
+       .pre_exit = cttimeout_net_pre_exit,
        .exit   = cttimeout_net_exit,
        .id     = &nfct_timeout_id,
        .size   = sizeof(struct nfct_timeout_pernet),