Skip to content

Commit 9e41f26

Browse files
author
Jozsef Kadlecsik
committed
netfilter: ipset: Count non-static extension memory for userspace
Non-static (i.e. comment) extension was not counted into the memory size. A new internal counter is introduced for this. In the case of the hash types the sizes of the arrays are counted there as well so that we can avoid to scan the whole set when just the header data is requested. Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
1 parent 702b71e commit 9e41f26

File tree

6 files changed

+32
-21
lines changed

6 files changed

+32
-21
lines changed

include/linux/netfilter/ipset/ip_set.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ enum ip_set_ext_id {
7979
IPSET_EXT_ID_MAX,
8080
};
8181

82+
struct ip_set;
83+
8284
/* Extension type */
8385
struct ip_set_ext_type {
8486
/* Destroy extension private data (can be NULL) */
85-
void (*destroy)(void *ext);
87+
void (*destroy)(struct ip_set *set, void *ext);
8688
enum ip_set_extension type;
8789
enum ipset_cadt_flags flag;
8890
/* Size and minimal alignment */
@@ -252,6 +254,8 @@ struct ip_set {
252254
u32 timeout;
253255
/* Number of elements (vs timeout) */
254256
u32 elements;
257+
/* Size of the dynamic extensions (vs timeout) */
258+
size_t ext_size;
255259
/* Element data size */
256260
size_t dsize;
257261
/* Offsets to extensions in elements */
@@ -268,7 +272,7 @@ ip_set_ext_destroy(struct ip_set *set, void *data)
268272
*/
269273
if (SET_WITH_COMMENT(set))
270274
ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(
271-
ext_comment(data, set));
275+
set, ext_comment(data, set));
272276
}
273277

274278
static inline int

include/linux/netfilter/ipset/ip_set_comment.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ ip_set_comment_uget(struct nlattr *tb)
2020
* The kadt functions don't use the comment extensions in any way.
2121
*/
2222
static inline void
23-
ip_set_init_comment(struct ip_set_comment *comment,
23+
ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
2424
const struct ip_set_ext *ext)
2525
{
2626
struct ip_set_comment_rcu *c = rcu_dereference_protected(comment->c, 1);
2727
size_t len = ext->comment ? strlen(ext->comment) : 0;
2828

2929
if (unlikely(c)) {
30+
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
3031
kfree_rcu(c, rcu);
3132
rcu_assign_pointer(comment->c, NULL);
3233
}
@@ -38,6 +39,7 @@ ip_set_init_comment(struct ip_set_comment *comment,
3839
if (unlikely(!c))
3940
return;
4041
strlcpy(c->str, ext->comment, len + 1);
42+
set->ext_size += sizeof(*c) + strlen(c->str) + 1;
4143
rcu_assign_pointer(comment->c, c);
4244
}
4345

@@ -58,13 +60,14 @@ ip_set_put_comment(struct sk_buff *skb, const struct ip_set_comment *comment)
5860
* of the set data anymore.
5961
*/
6062
static inline void
61-
ip_set_comment_free(struct ip_set_comment *comment)
63+
ip_set_comment_free(struct ip_set *set, struct ip_set_comment *comment)
6264
{
6365
struct ip_set_comment_rcu *c;
6466

6567
c = rcu_dereference_protected(comment->c, 1);
6668
if (unlikely(!c))
6769
return;
70+
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
6871
kfree_rcu(c, rcu);
6972
rcu_assign_pointer(comment->c, NULL);
7073
}

net/netfilter/ipset/ip_set_bitmap_gen.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ mtype_flush(struct ip_set *set)
8484
mtype_ext_cleanup(set);
8585
memset(map->members, 0, map->memsize);
8686
set->elements = 0;
87+
set->ext_size = 0;
8788
}
8889

8990
/* Calculate the actual memory size of the set data */
@@ -99,7 +100,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
99100
{
100101
const struct mtype *map = set->data;
101102
struct nlattr *nested;
102-
size_t memsize = mtype_memsize(map, set->dsize);
103+
size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
103104

104105
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
105106
if (!nested)
@@ -173,7 +174,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
173174
if (SET_WITH_COUNTER(set))
174175
ip_set_init_counter(ext_counter(x, set), ext);
175176
if (SET_WITH_COMMENT(set))
176-
ip_set_init_comment(ext_comment(x, set), ext);
177+
ip_set_init_comment(set, ext_comment(x, set), ext);
177178
if (SET_WITH_SKBINFO(set))
178179
ip_set_init_skbinfo(ext_skbinfo(x, set), ext);
179180

net/netfilter/ipset/ip_set_core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
324324
}
325325
EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
326326

327-
typedef void (*destroyer)(void *);
327+
typedef void (*destroyer)(struct ip_set *, void *);
328328
/* ipset data extension types, in size order */
329329

330330
const struct ip_set_ext_type ip_set_extensions[] = {

net/netfilter/ipset/ip_set_hash_gen.h

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -343,21 +343,13 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
343343
/* Calculate the actual memory size of the set data */
344344
static size_t
345345
mtype_ahash_memsize(const struct htype *h, const struct htable *t,
346-
u8 nets_length, size_t dsize)
346+
u8 nets_length)
347347
{
348-
u32 i;
349-
struct hbucket *n;
350348
size_t memsize = sizeof(*h) + sizeof(*t);
351349

352350
#ifdef IP_SET_HASH_WITH_NETS
353351
memsize += sizeof(struct net_prefixes) * nets_length;
354352
#endif
355-
for (i = 0; i < jhash_size(t->htable_bits); i++) {
356-
n = rcu_dereference_bh(hbucket(t, i));
357-
if (!n)
358-
continue;
359-
memsize += sizeof(struct hbucket) + n->size * dsize;
360-
}
361353

362354
return memsize;
363355
}
@@ -400,6 +392,7 @@ mtype_flush(struct ip_set *set)
400392
memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family));
401393
#endif
402394
set->elements = 0;
395+
set->ext_size = 0;
403396
}
404397

405398
/* Destroy the hashtable part of the set */
@@ -531,6 +524,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
531524
d++;
532525
}
533526
tmp->pos = d;
527+
set->ext_size -= AHASH_INIT_SIZE * dsize;
534528
rcu_assign_pointer(hbucket(t, i), tmp);
535529
kfree_rcu(n, rcu);
536530
}
@@ -562,7 +556,7 @@ mtype_resize(struct ip_set *set, bool retried)
562556
struct htype *h = set->data;
563557
struct htable *t, *orig;
564558
u8 htable_bits;
565-
size_t dsize = set->dsize;
559+
size_t extsize, dsize = set->dsize;
566560
#ifdef IP_SET_HASH_WITH_NETS
567561
u8 flags;
568562
struct mtype_elem *tmp;
@@ -605,6 +599,7 @@ mtype_resize(struct ip_set *set, bool retried)
605599
/* There can't be another parallel resizing, but dumping is possible */
606600
atomic_set(&orig->ref, 1);
607601
atomic_inc(&orig->uref);
602+
extsize = 0;
608603
pr_debug("attempt to resize set %s from %u to %u, t %p\n",
609604
set->name, orig->htable_bits, htable_bits, orig);
610605
for (i = 0; i < jhash_size(orig->htable_bits); i++) {
@@ -635,6 +630,7 @@ mtype_resize(struct ip_set *set, bool retried)
635630
goto cleanup;
636631
}
637632
m->size = AHASH_INIT_SIZE;
633+
extsize = sizeof(*m) + AHASH_INIT_SIZE * dsize;
638634
RCU_INIT_POINTER(hbucket(t, key), m);
639635
} else if (m->pos >= m->size) {
640636
struct hbucket *ht;
@@ -654,6 +650,7 @@ mtype_resize(struct ip_set *set, bool retried)
654650
memcpy(ht, m, sizeof(struct hbucket) +
655651
m->size * dsize);
656652
ht->size = m->size + AHASH_INIT_SIZE;
653+
extsize += AHASH_INIT_SIZE * dsize;
657654
kfree(m);
658655
m = ht;
659656
RCU_INIT_POINTER(hbucket(t, key), ht);
@@ -667,6 +664,7 @@ mtype_resize(struct ip_set *set, bool retried)
667664
}
668665
}
669666
rcu_assign_pointer(h->table, t);
667+
set->ext_size = extsize;
670668

671669
spin_unlock_bh(&set->lock);
672670

@@ -740,6 +738,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
740738
if (!n)
741739
return -ENOMEM;
742740
n->size = AHASH_INIT_SIZE;
741+
set->ext_size += sizeof(*n) + AHASH_INIT_SIZE * set->dsize;
743742
goto copy_elem;
744743
}
745744
for (i = 0; i < n->pos; i++) {
@@ -803,6 +802,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
803802
memcpy(n, old, sizeof(struct hbucket) +
804803
old->size * set->dsize);
805804
n->size = old->size + AHASH_INIT_SIZE;
805+
set->ext_size += AHASH_INIT_SIZE * set->dsize;
806806
}
807807

808808
copy_elem:
@@ -823,7 +823,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
823823
if (SET_WITH_COUNTER(set))
824824
ip_set_init_counter(ext_counter(data, set), ext);
825825
if (SET_WITH_COMMENT(set))
826-
ip_set_init_comment(ext_comment(data, set), ext);
826+
ip_set_init_comment(set, ext_comment(data, set), ext);
827827
if (SET_WITH_SKBINFO(set))
828828
ip_set_init_skbinfo(ext_skbinfo(data, set), ext);
829829
/* Must come last for the case when timed out entry is reused */
@@ -895,6 +895,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
895895
k++;
896896
}
897897
if (n->pos == 0 && k == 0) {
898+
set->ext_size -= sizeof(*n) + n->size * dsize;
898899
rcu_assign_pointer(hbucket(t, key), NULL);
899900
kfree_rcu(n, rcu);
900901
} else if (k >= AHASH_INIT_SIZE) {
@@ -913,6 +914,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
913914
k++;
914915
}
915916
tmp->pos = k;
917+
set->ext_size -= AHASH_INIT_SIZE * dsize;
916918
rcu_assign_pointer(hbucket(t, key), tmp);
917919
kfree_rcu(n, rcu);
918920
}
@@ -1061,7 +1063,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
10611063

10621064
rcu_read_lock_bh();
10631065
t = rcu_dereference_bh_nfnl(h->table);
1064-
memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize);
1066+
memsize = mtype_ahash_memsize(h, t, NLEN(set->family)) + set->ext_size;
10651067
htable_bits = t->htable_bits;
10661068
rcu_read_unlock_bh();
10671069

net/netfilter/ipset/ip_set_list_set.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext,
228228
if (SET_WITH_COUNTER(set))
229229
ip_set_init_counter(ext_counter(e, set), ext);
230230
if (SET_WITH_COMMENT(set))
231-
ip_set_init_comment(ext_comment(e, set), ext);
231+
ip_set_init_comment(set, ext_comment(e, set), ext);
232232
if (SET_WITH_SKBINFO(set))
233233
ip_set_init_skbinfo(ext_skbinfo(e, set), ext);
234234
/* Update timeout last */
@@ -422,6 +422,7 @@ list_set_flush(struct ip_set *set)
422422
list_for_each_entry_safe(e, n, &map->members, list)
423423
list_set_del(set, e);
424424
set->elements = 0;
425+
set->ext_size = 0;
425426
}
426427

427428
static void
@@ -467,7 +468,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
467468
{
468469
const struct list_set *map = set->data;
469470
struct nlattr *nested;
470-
size_t memsize = list_set_memsize(map, set->dsize);
471+
size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
471472

472473
nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
473474
if (!nested)

0 commit comments

Comments
 (0)