summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>2013-09-14 11:05:53 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2013-09-15 14:14:23 +0200
commitf4ca2c3025daafe8fe99f098137e1dc03c6cae5a (patch)
tree181b58bb8793f27dc6f2b8fe72ca313a2381443f
parent5b360d3eae1dd7c518bae6bf4620c409542db736 (diff)
netfilter: ipset: Fix serious failure in CIDR tracking
This fixes a serious bug affecting all hash types with a net element - specifically, if a CIDR value is deleted such that none of the same size exist any more, all larger (less-specific) values will then fail to match. Adding back any prefix with a CIDR equal to or more specific than the one deleted will fix it. Steps to reproduce: ipset -N test hash:net ipset -A test 1.1.0.0/16 ipset -A test 2.2.2.0/24 ipset -T test 1.1.1.1 #1.1.1.1 IS in set ipset -D test 2.2.2.0/24 ipset -T test 1.1.1.1 #1.1.1.1 IS NOT in set This is due to the fact that the nets counter was unconditionally decremented prior to the iteration that shifts up the entries. Now, we first check if there is a proceeding entry and if not, decrement it and return. Otherwise, we proceed to iterate and then zero the last element, which, in most cases, will already be zero. Signed-off-by: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa> Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_gen.h24
1 files changed, 14 insertions, 10 deletions
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index 7a5b776..3add0bf 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -307,18 +307,22 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
static void
mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
{
- u8 i, j;
+ u8 i, j, net_end = nets_length - 1;
- for (i = 0; i < nets_length - 1 && h->nets[i].cidr[n] != cidr; i++)
- ;
- h->nets[i].nets[n]--;
-
- if (h->nets[i].nets[n] != 0)
+ for (i = 0; i < nets_length; i++) {
+ if (h->nets[i].cidr[n] != cidr)
+ continue;
+ if (h->nets[i].nets[n] > 1 || i == net_end ||
+ h->nets[i + 1].nets[n] == 0) {
+ h->nets[i].nets[n]--;
+ return;
+ }
+ for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
+ h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
+ h->nets[j].nets[n] = h->nets[j + 1].nets[n];
+ }
+ h->nets[j].nets[n] = 0;
return;
-
- for (j = i; j < nets_length - 1 && h->nets[j].nets[n]; j++) {
- h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
- h->nets[j].nets[n] = h->nets[j + 1].nets[n];
}
}
#endif