[PATCH] netfilter: ipset: ipset list may return wrong member count on bitmap types

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We fixed a similar problem before in commit 7f4f7dd4417d ("netfilter:
ipset: ipset list may return wrong member count for set with timeout").
The same issue exists in ip_set_bitmap_gen.h as well.

Test case:

$ ipset create test bitmap:ip range 10.0.0.0/8 netmask 24 timeout 5
$ ipset add test 10.0.0.0
$ ipset add test 10.255.255.255
$ sleep 5s

$ ipset list test
Name: test
Type: bitmap:ip
Revision: 3
Header: range 10.0.0.0-10.255.255.255 netmask 24 timeout 5
Size in memory: 532568
References: 0
Number of entries: 2
Members:

We return "Number of entries: 2" but no members are listed. That is
because when we run mtype_head the garbage collector hasn't run yet, but
mtype_list checks and cleans up members with expired timeout. To avoid
this we can run mtype_expire before printing the number of elements in
mytype_head().

Reviewed-by: Joshua Hunt <johunt@xxxxxxxxxx>
Signed-off-by: Vishwanath Pai <vpai@xxxxxxxxxx>
---
 net/netfilter/ipset/ip_set_bitmap_gen.h | 46 ++++++++++++++++++-------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
index 26ab0e9612d8..dd871305bd6e 100644
--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -28,6 +28,7 @@
 #define mtype_del		IPSET_TOKEN(MTYPE, _del)
 #define mtype_list		IPSET_TOKEN(MTYPE, _list)
 #define mtype_gc		IPSET_TOKEN(MTYPE, _gc)
+#define mtype_expire		IPSET_TOKEN(MTYPE, _expire)
 #define mtype			MTYPE
 
 #define get_ext(set, map, id)	((map)->extensions + ((set)->dsize * (id)))
@@ -88,13 +89,44 @@ mtype_memsize(const struct mtype *map, size_t dsize)
 	       map->elements * dsize;
 }
 
+/* We should grab set->lock before calling this function */
+static void
+mtype_expire(struct ip_set *set)
+{
+	struct mtype *map = set->data;
+	void *x;
+	u32 id;
+
+	for (id = 0; id < map->elements; id++)
+		if (mtype_gc_test(id, map, set->dsize)) {
+			x = get_ext(set, map, id);
+			if (ip_set_timeout_expired(ext_timeout(x, set))) {
+				clear_bit(id, map->members);
+				ip_set_ext_destroy(set, x);
+				set->elements--;
+			}
+		}
+}
+
 static int
 mtype_head(struct ip_set *set, struct sk_buff *skb)
 {
 	const struct mtype *map = set->data;
 	struct nlattr *nested;
-	size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
+	size_t memsize;
+
+	/* If any members have expired, set->elements will be wrong,
+	 * mytype_expire function will update it with the right count.
+	 * set->elements can still be incorrect in the case of a huge set
+	 * because elements can timeout during set->list().
+	 */
+	if (SET_WITH_TIMEOUT(set)) {
+		spin_lock_bh(&set->lock);
+		mtype_expire(set);
+		spin_unlock_bh(&set->lock);
+	}
 
+	memsize = mtype_memsize(map, set->dsize) + set->ext_size;
 	nested = nla_nest_start(skb, IPSET_ATTR_DATA);
 	if (!nested)
 		goto nla_put_failure;
@@ -266,22 +298,12 @@ mtype_gc(struct timer_list *t)
 {
 	struct mtype *map = from_timer(map, t, gc);
 	struct ip_set *set = map->set;
-	void *x;
-	u32 id;
 
 	/* We run parallel with other readers (test element)
 	 * but adding/deleting new entries is locked out
 	 */
 	spin_lock_bh(&set->lock);
-	for (id = 0; id < map->elements; id++)
-		if (mtype_gc_test(id, map, set->dsize)) {
-			x = get_ext(set, map, id);
-			if (ip_set_timeout_expired(ext_timeout(x, set))) {
-				clear_bit(id, map->members);
-				ip_set_ext_destroy(set, x);
-				set->elements--;
-			}
-		}
+	mtype_expire(set);
 	spin_unlock_bh(&set->lock);
 
 	map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
-- 
2.25.1




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux