]> granicus.if.org Git - ipset/commitdiff
Fix cidr handling for hash:*net* types
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 13 Mar 2015 20:18:58 +0000 (21:18 +0100)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 13 Mar 2015 20:18:58 +0000 (21:18 +0100)
Commit 092d67cda9ad4 broke the cidr handling for the hash:*net* types
when the sets were used by the SET target: entries with invalid cidr
values were added to the sets. Reported by Jonathan Johnson.

Testsuite entry is added to verify the fix.

kernel/include/linux/netfilter/ipset/ip_set.h
kernel/net/netfilter/ipset/ip_set_hash_gen.h
kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
kernel/net/netfilter/ipset/ip_set_hash_net.c
kernel/net/netfilter/ipset/ip_set_hash_netiface.c
kernel/net/netfilter/ipset/ip_set_hash_netnet.c
kernel/net/netfilter/ipset/ip_set_hash_netport.c
kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
tests/iptables.sh
tests/match_target.t
tests/resizet.sh

index e62bbe80d2d204d8b6dd944b564a70b30a68836c..e5adf9e9d12a1e5d5661d6902b55fc5a1b0af855 100644 (file)
@@ -570,8 +570,6 @@ ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set,
        { .bytes = ULLONG_MAX, .packets = ULLONG_MAX,   \
          .timeout = (set)->timeout }
 
-#define IP_SET_INIT_CIDR(a, b) ((a) ? (a) : (b))
-
 #define IPSET_CONCAT(a, b)             a##b
 #define IPSET_TOKEN(a, b)              IPSET_CONCAT(a, b)
 
index 9570dee7f18202ddf572ee4ca6214005c9d0ad54..85cac70e90045e3a489e2d7cbf66e2487c8fe7bb 100644 (file)
@@ -134,17 +134,21 @@ htable_bits(u32 hashsize)
 #endif
 
 /* cidr + 1 is stored in net_prefixes to support /0 */
-#define SCIDR(cidr, i)         (__CIDR(cidr, i) + 1)
+#define NCIDR_PUT(cidr)                ((cidr) + 1)
+#define NCIDR_GET(cidr)                ((cidr) - 1)
 
 #ifdef IP_SET_HASH_WITH_NETS_PACKED
 /* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */
-#define GCIDR(cidr, i)         (__CIDR(cidr, i) + 1)
-#define NCIDR(cidr)            (cidr)
+#define DCIDR_PUT(cidr)                ((cidr) - 1)
+#define DCIDR_GET(cidr, i)     (__CIDR(cidr, i) + 1)
 #else
-#define GCIDR(cidr, i)         (__CIDR(cidr, i))
-#define NCIDR(cidr)            (cidr - 1)
+#define DCIDR_PUT(cidr)                (cidr)
+#define DCIDR_GET(cidr, i)     __CIDR(cidr, i)
 #endif
 
+#define INIT_CIDR(cidr, host_mask)     \
+       DCIDR_PUT(((cidr) ? NCIDR_GET(cidr) : host_mask))
+
 #define SET_HOST_MASK(family)  (family == AF_INET ? 32 : 128)
 
 #ifdef IP_SET_HASH_WITH_NET0
@@ -275,7 +279,7 @@ struct htype {
 
 #ifdef IP_SET_HASH_WITH_NETS
 /* Network cidr size book keeping when the hash stores different
- * sized networks
+ * sized networks. cidr == real cidr + 1 to support /0.
  */
 static void
 mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
@@ -477,8 +481,10 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
                                pr_debug("expired %u/%u\n", i, j);
 #ifdef IP_SET_HASH_WITH_NETS
                                for (k = 0; k < IPSET_NET_COUNT; k++)
-                                       mtype_del_cidr(h, SCIDR(data->cidr, k),
-                                                      nets_length, k);
+                                       mtype_del_cidr(h,
+                                               NCIDR_PUT(DCIDR_GET(data->cidr,
+                                                                   k)),
+                                               nets_length, k);
 #endif
                                ip_set_ext_destroy(set, data);
                                clear_bit(j, n->used);
@@ -742,8 +748,9 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                if (!deleted) {
 #ifdef IP_SET_HASH_WITH_NETS
                        for (i = 0; i < IPSET_NET_COUNT; i++)
-                               mtype_del_cidr(h, SCIDR(data->cidr, i),
-                                              NLEN(set->family), i);
+                               mtype_del_cidr(h,
+                                       NCIDR_PUT(DCIDR_GET(data->cidr, i)),
+                                       NLEN(set->family), i);
 #endif
                        ip_set_ext_destroy(set, data);
                        h->elements--;
@@ -778,7 +785,7 @@ copy_data:
        h->elements++;
 #ifdef IP_SET_HASH_WITH_NETS
        for (i = 0; i < IPSET_NET_COUNT; i++)
-               mtype_add_cidr(h, SCIDR(d->cidr, i),
+               mtype_add_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, i)),
                               NLEN(set->family), i);
 #endif
        memcpy(data, d, sizeof(struct mtype_elem));
@@ -849,8 +856,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
                h->elements--;
 #ifdef IP_SET_HASH_WITH_NETS
                for (j = 0; j < IPSET_NET_COUNT; j++)
-                       mtype_del_cidr(h, SCIDR(d->cidr, j), NLEN(set->family),
-                                      j);
+                       mtype_del_cidr(h, NCIDR_PUT(DCIDR_GET(d->cidr, j)),
+                                      NLEN(set->family), j);
 #endif
                ip_set_ext_destroy(set, data);
 
@@ -926,12 +933,13 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
        for (; j < nets_length && h->nets[j].cidr[0] && !multi; j++) {
 #if IPSET_NET_COUNT == 2
                mtype_data_reset_elem(d, &orig);
-               mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0]), false);
+               mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]), false);
                for (k = 0; k < nets_length && h->nets[k].cidr[1] && !multi;
                     k++) {
-                       mtype_data_netmask(d, NCIDR(h->nets[k].cidr[1]), true);
+                       mtype_data_netmask(d, NCIDR_GET(h->nets[k].cidr[1]),
+                                          true);
 #else
-               mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0]));
+               mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]));
 #endif
                key = HKEY(d, h->initval, t->htable_bits);
                n =  rcu_dereference_bh(hbucket(t, key));
@@ -983,7 +991,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
         * try all possible network sizes
         */
        for (i = 0; i < IPSET_NET_COUNT; i++)
-               if (GCIDR(d->cidr, i) != SET_HOST_MASK(set->family))
+               if (DCIDR_GET(d->cidr, i) != SET_HOST_MASK(set->family))
                        break;
        if (i == IPSET_NET_COUNT) {
                ret = mtype_test_cidrs(set, d, ext, mext, flags);
index 129b9b79c44f31fb3738e846b593f651e5288c8c..3ce222b80efba39aacb6e2238b714cc662e1ad67 100644 (file)
@@ -142,7 +142,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
@@ -401,7 +401,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_ipportnet *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
index 4e72e47fc8ad87b68969e0a85e6240032f342192..e42ac91f8d23944736171baf607861f462601b2a 100644 (file)
@@ -121,7 +121,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
@@ -295,7 +295,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_net *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net6_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
index ea134cabd21b865b461a6d7e4e76070d4bea2b59..5471dc5be08be19c9f75c04b937b3aead033e2f9 100644 (file)
@@ -143,7 +143,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
@@ -363,7 +363,7 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netiface *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface6_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
                .elem = 1,
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
index 158c53003a2877648e98c14dc391757e5062cdf4..aeea18957c1682513ac7ed02ff7f423ce86e1c33 100644 (file)
@@ -142,8 +142,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
-       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
-       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
        if (adt == IPSET_TEST)
                e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
 
@@ -376,8 +376,8 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netnet6_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
-       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
-       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
        if (adt == IPSET_TEST)
                e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK;
 
index 40c3ae81c85d42047961270ed5c9d984de0e0008..fcd39f60ddc7ae7556fed9fe7440abb3cb62556f 100644 (file)
@@ -137,7 +137,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
@@ -360,7 +360,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct hash_netport *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem e = {
-               .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1,
+               .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
        };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
index 6e0ddb58fa7f2c9fdd4400f07c3276a920e4686b..2837ff9b549a55a95c1b98c1be18077dd7d991af 100644 (file)
@@ -153,8 +153,8 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netportnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
-       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
-       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
        if (adt == IPSET_TEST)
                e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
 
@@ -434,8 +434,8 @@ hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        struct hash_netportnet6_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
-       e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
-       e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
+       e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
+       e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
        if (adt == IPSET_TEST)
                e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;
 
index c5b9bf7ecf012e865f09b411810fe25130b2f0c2..7ea90e025b6f539963348a7d32e78a2343e0fb97 100755 (executable)
@@ -95,6 +95,11 @@ del)
        $cmd -F INPUT
        $cmd -A INPUT -j SET --del-set ipport src,src
        ;;
+add)
+       $ipset n test hash:net $family 2>/dev/null
+       $cmd -F INPUT
+       $cmd -A INPUT -j SET --add-set test src
+       ;;
 timeout)
        $ipset n test hash:ip,port timeout 2
        $cmd -A INPUT -j SET --add-set test src,src --timeout 10 --exist
index 1739fae766644d71c9ac80c771502ddd8ac90bb9..02a0ea380cfaca56f62fe728a8e3e6da878c07bb 100644 (file)
 0 ./check_klog.sh 10.255.255.64 udp 1025 mark
 # Destroy sets and rules
 0 ./iptables.sh inet stop
+# Create test set and iptables rules
+0 ./iptables.sh inet add
+# Send probe packet from 10.255.255.64,udp:1025
+0 sendip -p ipv4 -id 127.0.0.1 -is 10.255.255.64 -p udp -ud 80 -us 1025 127.0.0.1
+# Check that 10.255.255.64 is added to the set
+0 ipset t test 10.255.255.64
+# Flush set
+0 ipset f test
+# Add a /24 network to the set
+0 ipset a test 1.1.1.0/24
+# Send probe packet from 10.255.255.64,udp:1025 again
+0 sendip -p ipv4 -id 127.0.0.1 -is 10.255.255.64 -p udp -ud 80 -us 1025 127.0.0.1
+# Check that 10.255.255.0/24 is added to the set
+0 ipset t test 10.255.255.0/24
+# Destroy sets and rules
+0 ./iptables.sh inet stop
 # eof
index b5f7fdc3c5b37a36c797cbf86e4f6237d89ae7f4..7dc309f112c46b11e537d312b6af55ae78a55543 100755 (executable)
@@ -103,7 +103,7 @@ case "$2" in
        ;;
 esac
 $ipset l test | grep ^$ip | while read x y z; do
-    if [ $z -lt 10 -o $z -gt 99 ]; then
+    if [ $z -lt 10 -o $z -gt 100 ]; then
        exit 1
     fi
 done