netfilter: evict stale entries on netlink dumps
authorFlorian Westphal <fw@strlen.de>
Thu, 25 Aug 2016 13:33:32 +0000 (15:33 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 30 Aug 2016 09:43:09 +0000 (11:43 +0200)
When dumping we already have to look at the entire table, so we might
as well toss those entries whose timeout value is in the past.

We also look at every entry during resize operations.
However, eviction there is not as simple because we hold the
global resize lock so we can't evict without adding a 'expired' list
to drop from later.  Considering that resizes are very rare it doesn't
seem worth doing it.

Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_conntrack_netlink.c

index 81fd34ce0a57e406d99d346a7dc7aa7b005638e4..dedbe4b33f381e28f8cea275d6da07b410d858d3 100644 (file)
@@ -815,14 +815,23 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        struct hlist_nulls_node *n;
        struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
-       int res;
+       struct nf_conn *nf_ct_evict[8];
+       int res, i;
        spinlock_t *lockp;
 
        last = (struct nf_conn *)cb->args[1];
+       i = 0;
 
        local_bh_disable();
        for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
 restart:
+               while (i) {
+                       i--;
+                       if (nf_ct_should_gc(nf_ct_evict[i]))
+                               nf_ct_kill(nf_ct_evict[i]);
+                       nf_ct_put(nf_ct_evict[i]);
+               }
+
                lockp = &nf_conntrack_locks[cb->args[0] % CONNTRACK_LOCKS];
                nf_conntrack_lock(lockp);
                if (cb->args[0] >= nf_conntrack_htable_size) {
@@ -834,6 +843,13 @@ restart:
                        if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
+                       if (nf_ct_is_expired(ct)) {
+                               if (i < ARRAY_SIZE(nf_ct_evict) &&
+                                   atomic_inc_not_zero(&ct->ct_general.use))
+                                       nf_ct_evict[i++] = ct;
+                               continue;
+                       }
+
                        if (!net_eq(net, nf_ct_net(ct)))
                                continue;
 
@@ -875,6 +891,13 @@ out:
        if (last)
                nf_ct_put(last);
 
+       while (i) {
+               i--;
+               if (nf_ct_should_gc(nf_ct_evict[i]))
+                       nf_ct_kill(nf_ct_evict[i]);
+               nf_ct_put(nf_ct_evict[i]);
+       }
+
        return skb->len;
 }