Skip to content

Commit cb2b36f

Browse files
YiHungWeiummakynes
authored andcommitted
netfilter: nf_conncount: Switch to plain list
Original patch is from Florian Westphal. This patch switches from hlist to plain list to store the list of connections with the same filtering key in nf_conncount. With the plain list, we can insert new connections at the tail, so over time the beginning of list holds long-running connections and those are expired, while the newly creates ones are at the end. Later on, we could probably move checked ones to the end of the list, so the next run has higher chance to reclaim stale entries in the front. Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 2a406e8 commit cb2b36f

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

include/net/netfilter/nf_conntrack_count.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
#ifndef _NF_CONNTRACK_COUNT_H
22
#define _NF_CONNTRACK_COUNT_H
33

4+
#include <linux/list.h>
5+
46
struct nf_conncount_data;
57

8+
struct nf_conncount_list {
9+
struct list_head head; /* connections with the same filtering key */
10+
unsigned int count; /* length of list */
11+
};
12+
613
struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int family,
714
unsigned int keylen);
815
void nf_conncount_destroy(struct net *net, unsigned int family,
@@ -14,15 +21,17 @@ unsigned int nf_conncount_count(struct net *net,
1421
const struct nf_conntrack_tuple *tuple,
1522
const struct nf_conntrack_zone *zone);
1623

17-
unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
24+
unsigned int nf_conncount_lookup(struct net *net, struct nf_conncount_list *list,
1825
const struct nf_conntrack_tuple *tuple,
1926
const struct nf_conntrack_zone *zone,
2027
bool *addit);
2128

22-
bool nf_conncount_add(struct hlist_head *head,
29+
void nf_conncount_list_init(struct nf_conncount_list *list);
30+
31+
bool nf_conncount_add(struct nf_conncount_list *list,
2332
const struct nf_conntrack_tuple *tuple,
2433
const struct nf_conntrack_zone *zone);
2534

26-
void nf_conncount_cache_free(struct hlist_head *hhead);
35+
void nf_conncount_cache_free(struct nf_conncount_list *list);
2736

2837
#endif

net/netfilter/nf_conncount.c

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
/* we will save the tuples of all connections we care about */
4646
struct nf_conncount_tuple {
47-
struct hlist_node node;
47+
struct list_head node;
4848
struct nf_conntrack_tuple tuple;
4949
struct nf_conntrack_zone zone;
5050
int cpu;
@@ -53,7 +53,7 @@ struct nf_conncount_tuple {
5353

5454
struct nf_conncount_rb {
5555
struct rb_node node;
56-
struct hlist_head hhead; /* connections/hosts in same subnet */
56+
struct nf_conncount_list list;
5757
u32 key[MAX_KEYLEN];
5858
};
5959

@@ -82,26 +82,42 @@ static int key_diff(const u32 *a, const u32 *b, unsigned int klen)
8282
return memcmp(a, b, klen * sizeof(u32));
8383
}
8484

85-
bool nf_conncount_add(struct hlist_head *head,
85+
bool nf_conncount_add(struct nf_conncount_list *list,
8686
const struct nf_conntrack_tuple *tuple,
8787
const struct nf_conntrack_zone *zone)
8888
{
8989
struct nf_conncount_tuple *conn;
9090

91+
if (WARN_ON_ONCE(list->count > INT_MAX))
92+
return false;
93+
9194
conn = kmem_cache_alloc(conncount_conn_cachep, GFP_ATOMIC);
9295
if (conn == NULL)
9396
return false;
9497
conn->tuple = *tuple;
9598
conn->zone = *zone;
9699
conn->cpu = raw_smp_processor_id();
97100
conn->jiffies32 = (u32)jiffies;
98-
hlist_add_head(&conn->node, head);
101+
list_add_tail(&conn->node, &list->head);
102+
list->count++;
99103
return true;
100104
}
101105
EXPORT_SYMBOL_GPL(nf_conncount_add);
102106

107+
static void conn_free(struct nf_conncount_list *list,
108+
struct nf_conncount_tuple *conn)
109+
{
110+
if (WARN_ON_ONCE(list->count == 0))
111+
return;
112+
113+
list->count--;
114+
list_del(&conn->node);
115+
kmem_cache_free(conncount_conn_cachep, conn);
116+
}
117+
103118
static const struct nf_conntrack_tuple_hash *
104-
find_or_evict(struct net *net, struct nf_conncount_tuple *conn)
119+
find_or_evict(struct net *net, struct nf_conncount_list *list,
120+
struct nf_conncount_tuple *conn)
105121
{
106122
const struct nf_conntrack_tuple_hash *found;
107123
unsigned long a, b;
@@ -121,30 +137,29 @@ find_or_evict(struct net *net, struct nf_conncount_tuple *conn)
121137
*/
122138
age = a - b;
123139
if (conn->cpu == cpu || age >= 2) {
124-
hlist_del(&conn->node);
125-
kmem_cache_free(conncount_conn_cachep, conn);
140+
conn_free(list, conn);
126141
return ERR_PTR(-ENOENT);
127142
}
128143

129144
return ERR_PTR(-EAGAIN);
130145
}
131146

132-
unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
147+
unsigned int nf_conncount_lookup(struct net *net,
148+
struct nf_conncount_list *list,
133149
const struct nf_conntrack_tuple *tuple,
134150
const struct nf_conntrack_zone *zone,
135151
bool *addit)
136152
{
137153
const struct nf_conntrack_tuple_hash *found;
138-
struct nf_conncount_tuple *conn;
154+
struct nf_conncount_tuple *conn, *conn_n;
139155
struct nf_conn *found_ct;
140-
struct hlist_node *n;
141156
unsigned int length = 0;
142157

143158
*addit = tuple ? true : false;
144159

145160
/* check the saved connections */
146-
hlist_for_each_entry_safe(conn, n, head, node) {
147-
found = find_or_evict(net, conn);
161+
list_for_each_entry_safe(conn, conn_n, &list->head, node) {
162+
found = find_or_evict(net, list, conn);
148163
if (IS_ERR(found)) {
149164
/* Not found, but might be about to be confirmed */
150165
if (PTR_ERR(found) == -EAGAIN) {
@@ -157,6 +172,7 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
157172
nf_ct_zone_id(zone, zone->dir))
158173
*addit = false;
159174
}
175+
160176
continue;
161177
}
162178

@@ -176,8 +192,7 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
176192
* closed already -> ditch it
177193
*/
178194
nf_ct_put(found_ct);
179-
hlist_del(&conn->node);
180-
kmem_cache_free(conncount_conn_cachep, conn);
195+
conn_free(list, conn);
181196
continue;
182197
}
183198

@@ -189,17 +204,23 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
189204
}
190205
EXPORT_SYMBOL_GPL(nf_conncount_lookup);
191206

207+
void nf_conncount_list_init(struct nf_conncount_list *list)
208+
{
209+
INIT_LIST_HEAD(&list->head);
210+
list->count = 1;
211+
}
212+
EXPORT_SYMBOL_GPL(nf_conncount_list_init);
213+
192214
static void nf_conncount_gc_list(struct net *net,
193-
struct nf_conncount_rb *rbconn)
215+
struct nf_conncount_list *list)
194216
{
195217
const struct nf_conntrack_tuple_hash *found;
196-
struct nf_conncount_tuple *conn;
197-
struct hlist_node *n;
218+
struct nf_conncount_tuple *conn, *conn_n;
198219
struct nf_conn *found_ct;
199220
unsigned int collected = 0;
200221

201-
hlist_for_each_entry_safe(conn, n, &rbconn->hhead, node) {
202-
found = find_or_evict(net, conn);
222+
list_for_each_entry_safe(conn, conn_n, &list->head, node) {
223+
found = find_or_evict(net, list, conn);
203224
if (IS_ERR(found)) {
204225
if (PTR_ERR(found) == -ENOENT)
205226
collected++;
@@ -213,8 +234,7 @@ static void nf_conncount_gc_list(struct net *net,
213234
* closed already -> ditch it
214235
*/
215236
nf_ct_put(found_ct);
216-
hlist_del(&conn->node);
217-
kmem_cache_free(conncount_conn_cachep, conn);
237+
conn_free(list, conn);
218238
collected++;
219239
continue;
220240
}
@@ -271,14 +291,14 @@ count_tree(struct net *net, struct rb_root *root,
271291
/* same source network -> be counted! */
272292
unsigned int count;
273293

274-
count = nf_conncount_lookup(net, &rbconn->hhead, tuple,
294+
count = nf_conncount_lookup(net, &rbconn->list, tuple,
275295
zone, &addit);
276296

277297
tree_nodes_free(root, gc_nodes, gc_count);
278298
if (!addit)
279299
return count;
280300

281-
if (!nf_conncount_add(&rbconn->hhead, tuple, zone))
301+
if (!nf_conncount_add(&rbconn->list, tuple, zone))
282302
return 0; /* hotdrop */
283303

284304
return count + 1;
@@ -287,8 +307,8 @@ count_tree(struct net *net, struct rb_root *root,
287307
if (no_gc || gc_count >= ARRAY_SIZE(gc_nodes))
288308
continue;
289309

290-
nf_conncount_gc_list(net, rbconn);
291-
if (hlist_empty(&rbconn->hhead))
310+
nf_conncount_gc_list(net, &rbconn->list);
311+
if (list_empty(&rbconn->list.head))
292312
gc_nodes[gc_count++] = rbconn;
293313
}
294314

@@ -322,8 +342,8 @@ count_tree(struct net *net, struct rb_root *root,
322342
conn->zone = *zone;
323343
memcpy(rbconn->key, key, sizeof(u32) * keylen);
324344

325-
INIT_HLIST_HEAD(&rbconn->hhead);
326-
hlist_add_head(&conn->node, &rbconn->hhead);
345+
nf_conncount_list_init(&rbconn->list);
346+
list_add(&conn->node, &rbconn->list.head);
327347

328348
rb_link_node(&rbconn->node, parent, rbnode);
329349
rb_insert_color(&rbconn->node, root);
@@ -388,12 +408,11 @@ struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int family
388408
}
389409
EXPORT_SYMBOL_GPL(nf_conncount_init);
390410

391-
void nf_conncount_cache_free(struct hlist_head *hhead)
411+
void nf_conncount_cache_free(struct nf_conncount_list *list)
392412
{
393-
struct nf_conncount_tuple *conn;
394-
struct hlist_node *n;
413+
struct nf_conncount_tuple *conn, *conn_n;
395414

396-
hlist_for_each_entry_safe(conn, n, hhead, node)
415+
list_for_each_entry_safe(conn, conn_n, &list->head, node)
397416
kmem_cache_free(conncount_conn_cachep, conn);
398417
}
399418
EXPORT_SYMBOL_GPL(nf_conncount_cache_free);
@@ -408,7 +427,7 @@ static void destroy_tree(struct rb_root *r)
408427

409428
rb_erase(node, r);
410429

411-
nf_conncount_cache_free(&rbconn->hhead);
430+
nf_conncount_cache_free(&rbconn->list);
412431

413432
kmem_cache_free(conncount_rb_cachep, rbconn);
414433
}

net/netfilter/nft_connlimit.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
#include <net/netfilter/nf_conntrack_zones.h>
1515

1616
struct nft_connlimit {
17-
spinlock_t lock;
18-
struct hlist_head hhead;
19-
u32 limit;
20-
bool invert;
17+
spinlock_t lock;
18+
struct nf_conncount_list list;
19+
u32 limit;
20+
bool invert;
2121
};
2222

2323
static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
@@ -46,13 +46,13 @@ static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
4646
}
4747

4848
spin_lock_bh(&priv->lock);
49-
count = nf_conncount_lookup(nft_net(pkt), &priv->hhead, tuple_ptr, zone,
49+
count = nf_conncount_lookup(nft_net(pkt), &priv->list, tuple_ptr, zone,
5050
&addit);
5151

5252
if (!addit)
5353
goto out;
5454

55-
if (!nf_conncount_add(&priv->hhead, tuple_ptr, zone)) {
55+
if (!nf_conncount_add(&priv->list, tuple_ptr, zone)) {
5656
regs->verdict.code = NF_DROP;
5757
spin_unlock_bh(&priv->lock);
5858
return;
@@ -88,7 +88,7 @@ static int nft_connlimit_do_init(const struct nft_ctx *ctx,
8888
}
8989

9090
spin_lock_init(&priv->lock);
91-
INIT_HLIST_HEAD(&priv->hhead);
91+
nf_conncount_list_init(&priv->list);
9292
priv->limit = limit;
9393
priv->invert = invert;
9494

@@ -99,7 +99,7 @@ static void nft_connlimit_do_destroy(const struct nft_ctx *ctx,
9999
struct nft_connlimit *priv)
100100
{
101101
nf_ct_netns_put(ctx->net, ctx->family);
102-
nf_conncount_cache_free(&priv->hhead);
102+
nf_conncount_cache_free(&priv->list);
103103
}
104104

105105
static int nft_connlimit_do_dump(struct sk_buff *skb,
@@ -213,7 +213,7 @@ static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src)
213213
struct nft_connlimit *priv_src = nft_expr_priv(src);
214214

215215
spin_lock_init(&priv_dst->lock);
216-
INIT_HLIST_HEAD(&priv_dst->hhead);
216+
nf_conncount_list_init(&priv_dst->list);
217217
priv_dst->limit = priv_src->limit;
218218
priv_dst->invert = priv_src->invert;
219219

@@ -225,7 +225,7 @@ static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
225225
{
226226
struct nft_connlimit *priv = nft_expr_priv(expr);
227227

228-
nf_conncount_cache_free(&priv->hhead);
228+
nf_conncount_cache_free(&priv->list);
229229
}
230230

231231
static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
@@ -234,9 +234,9 @@ static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
234234
bool addit, ret;
235235

236236
spin_lock_bh(&priv->lock);
237-
nf_conncount_lookup(net, &priv->hhead, NULL, &nf_ct_zone_dflt, &addit);
237+
nf_conncount_lookup(net, &priv->list, NULL, &nf_ct_zone_dflt, &addit);
238238

239-
ret = hlist_empty(&priv->hhead);
239+
ret = list_empty(&priv->list.head);
240240
spin_unlock_bh(&priv->lock);
241241

242242
return ret;

0 commit comments

Comments
 (0)