--- /dev/null
+From 2cf55125c64d64cc106e204d53b107094762dfdf Mon Sep 17 00:00:00 2001
+From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+Date: Mon, 16 Sep 2013 20:30:57 +0200
+Subject: netfilter: ipset: Fix serious failure in CIDR tracking
+
+From: Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>
+
+commit 2cf55125c64d64cc106e204d53b107094762dfdf upstream.
+
+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>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/netfilter/ipset/ip_set_hash_gen.h | 26 +++++++++++++++-----------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+--- a/net/netfilter/ipset/ip_set_hash_gen.h
++++ b/net/netfilter/ipset/ip_set_hash_gen.h
+@@ -325,18 +325,22 @@ mtype_add_cidr(struct htype *h, u8 cidr,
+ static void
+ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
+ {
+- u8 i, j;
++ u8 i, j, net_end = nets_length - 1;
+
+- for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++)
+- ;
+- h->nets[i].nets--;
+-
+- if (h->nets[i].nets != 0)
+- return;
+-
+- for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) {
+- h->nets[j].cidr = h->nets[j + 1].cidr;
+- h->nets[j].nets = h->nets[j + 1].nets;
++ for (i = 0; i < nets_length; i++) {
++ if (h->nets[i].cidr != cidr)
++ continue;
++ if (h->nets[i].nets > 1 || i == net_end ||
++ h->nets[i + 1].nets == 0) {
++ h->nets[i].nets--;
++ return;
++ }
++ for (j = i; j < net_end && h->nets[j].nets; j++) {
++ h->nets[j].cidr = h->nets[j + 1].cidr;
++ h->nets[j].nets = h->nets[j + 1].nets;
++ }
++ h->nets[j].nets = 0;
++ return;
+ }
+ }
+ #endif