]> git.ipfire.org Git - thirdparty/ipset.git/commitdiff
Exceptions support added to hash:*net* types
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 13 Jan 2012 21:52:44 +0000 (22:52 +0100)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Fri, 13 Jan 2012 21:52:44 +0000 (22:52 +0100)
The "nomatch" keyword and option is added to the hash:*net* types,
by which one can add exception entries to sets. Example:

ipset create test hash:net
ipset add test 192.168.0/24
ipset add test 192.168.0/30 nomatch

In this case the IP addresses from 192.168.0/24 except 192.168.0/30
match the elements of the set.

27 files changed:
include/libipset/data.h
include/libipset/linux_ip_set.h
kernel/include/linux/netfilter/ipset/ip_set.h
kernel/include/linux/netfilter/ipset/ip_set_ahash.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_netport.c
lib/data.c
lib/ipset_hash_ipportnet.c
lib/ipset_hash_net.c
lib/ipset_hash_netiface.c
lib/ipset_hash_netport.c
lib/parse.c
lib/print.c
lib/session.c
lib/types.c
src/ipset.8
tests/hash:ip,port,net.t
tests/hash:ip6,port,net6.t
tests/hash:net,iface.t
tests/hash:net,port.t
tests/hash:net.t
tests/hash:net6,port.t
tests/hash:net6.t
tests/ipportnethash.t
tests/nethash.t

index 3ba6f0a6ef810652fc55eb624891d7a1c716c1de..525cc6a78039c13619ccfb8ffffef4e553b572c6 100644 (file)
@@ -53,6 +53,7 @@ enum ipset_opt {
        IPSET_OPT_EXIST,
        IPSET_OPT_BEFORE,
        IPSET_OPT_PHYSDEV,
+       IPSET_OPT_NOMATCH,
        /* Internal options */
        IPSET_OPT_FLAGS = 48,   /* IPSET_FLAG_EXIST| */
        IPSET_OPT_CADT_FLAGS,   /* IPSET_FLAG_BEFORE| */
@@ -101,7 +102,8 @@ enum ipset_opt {
        | IPSET_FLAG(IPSET_OPT_IFACE) \
        | IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
        | IPSET_FLAG(IPSET_OPT_BEFORE) \
-       | IPSET_FLAG(IPSET_OPT_PHYSDEV))
+       | IPSET_FLAG(IPSET_OPT_PHYSDEV) \
+       | IPSET_FLAG(IPSET_OPT_NOMATCH))
 
 struct ipset_data;
 
index b336d43c7e8d4dd6f65f9c0e1a65f241e6c84aa1..008da06a590eea076a10644e0fcf0a307bf613d4 100644 (file)
@@ -150,6 +150,7 @@ enum ipset_cmd_flags {
        IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
        IPSET_FLAG_BIT_LIST_HEADER = 2,
        IPSET_FLAG_LIST_HEADER  = (1 << IPSET_FLAG_BIT_LIST_HEADER),
+       IPSET_FLAG_CMD_MAX = 15,        /* Lower half */
 };
 
 /* Flags at CADT attribute level */
@@ -158,6 +159,9 @@ enum ipset_cadt_flags {
        IPSET_FLAG_BEFORE       = (1 << IPSET_FLAG_BIT_BEFORE),
        IPSET_FLAG_BIT_PHYSDEV  = 1,
        IPSET_FLAG_PHYSDEV      = (1 << IPSET_FLAG_BIT_PHYSDEV),
+       IPSET_FLAG_BIT_NOMATCH  = 2,
+       IPSET_FLAG_NOMATCH      = (1 << IPSET_FLAG_BIT_NOMATCH),
+       IPSET_FLAG_CADT_MAX     = 15,   /* Upper half */
 };
 
 /* Commands with settype-specific attributes */
index e921766d3aff244a57093da73d0f2f4c117903b9..2f8e18a232273faa679a1d7ee60944b292f16de6 100644 (file)
@@ -150,6 +150,7 @@ enum ipset_cmd_flags {
        IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME),
        IPSET_FLAG_BIT_LIST_HEADER = 2,
        IPSET_FLAG_LIST_HEADER  = (1 << IPSET_FLAG_BIT_LIST_HEADER),
+       IPSET_FLAG_CMD_MAX = 15,        /* Lower half */
 };
 
 /* Flags at CADT attribute level */
@@ -158,6 +159,9 @@ enum ipset_cadt_flags {
        IPSET_FLAG_BEFORE       = (1 << IPSET_FLAG_BIT_BEFORE),
        IPSET_FLAG_BIT_PHYSDEV  = 1,
        IPSET_FLAG_PHYSDEV      = (1 << IPSET_FLAG_BIT_PHYSDEV),
+       IPSET_FLAG_BIT_NOMATCH  = 2,
+       IPSET_FLAG_NOMATCH      = (1 << IPSET_FLAG_BIT_NOMATCH),
+       IPSET_FLAG_CADT_MAX     = 15,   /* Upper half */
 };
 
 /* Commands with settype-specific attributes */
index bd1fc8d16851f7c2073565e02219a84191fcd7c8..0e5c3cf7618abf3254f14ee1ebd34ae15275a407 100644 (file)
@@ -113,6 +113,12 @@ htable_bits(u32 hashsize)
 }
 
 #ifdef IP_SET_HASH_WITH_NETS
+#ifdef IP_SET_HASH_WITH_NETS_PACKED
+/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
+#define CIDR(cidr)     (cidr + 1)
+#else
+#define CIDR(cidr)     (cidr)
+#endif
 
 #define SET_HOST_MASK(family)  (family == AF_INET ? 32 : 128)
 
@@ -262,6 +268,12 @@ ip_set_hash_destroy(struct ip_set *set)
 #define type_pf_data_list      TOKEN(TYPE, PF, _data_list)
 #define type_pf_data_tlist     TOKEN(TYPE, PF, _data_tlist)
 #define type_pf_data_next      TOKEN(TYPE, PF, _data_next)
+#define type_pf_data_flags     TOKEN(TYPE, PF, _data_flags)
+#ifdef IP_SET_HASH_WITH_NETS
+#define type_pf_data_match     TOKEN(TYPE, PF, _data_match)
+#else
+#define type_pf_data_match(d)  1
+#endif
 
 #define type_pf_elem           TOKEN(TYPE, PF, _elem)
 #define type_pf_telem          TOKEN(TYPE, PF, _telem)
@@ -308,8 +320,10 @@ ip_set_hash_destroy(struct ip_set *set)
  * we spare the maintenance of the internal counters. */
 static int
 type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value,
-                u8 ahash_max)
+                u8 ahash_max, u32 cadt_flags)
 {
+       struct type_pf_elem *data;
+
        if (n->pos >= n->size) {
                void *tmp;
 
@@ -330,7 +344,13 @@ type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value,
                n->value = tmp;
                n->size += AHASH_INIT_SIZE;
        }
-       type_pf_data_copy(ahash_data(n, n->pos++), value);
+       data = ahash_data(n, n->pos++);
+       type_pf_data_copy(data, value);
+#ifdef IP_SET_HASH_WITH_NETS
+       /* Resizing won't overwrite stored flags */
+       if (cadt_flags)
+               type_pf_data_flags(data, cadt_flags);
+#endif
        return 0;
 }
 
@@ -371,7 +391,7 @@ retry:
                for (j = 0; j < n->pos; j++) {
                        data = ahash_data(n, j);
                        m = hbucket(t, HKEY(data, h->initval, htable_bits));
-                       ret = type_pf_elem_add(m, data, AHASH_MAX(h));
+                       ret = type_pf_elem_add(m, data, AHASH_MAX(h), 0);
                        if (ret < 0) {
                                read_unlock_bh(&set->lock);
                                ahash_destroy(t);
@@ -409,6 +429,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
        struct hbucket *n;
        int i, ret = 0;
        u32 key, multi = 0;
+       u32 cadt_flags = flags >> 16;
 
        if (h->elements >= h->maxelem) {
                if (net_ratelimit())
@@ -423,11 +444,17 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++)
                if (type_pf_data_equal(ahash_data(n, i), d, &multi)) {
+#ifdef IP_SET_HASH_WITH_NETS
+                       if (flags & IPSET_FLAG_EXIST)
+                               /* Support overwriting just the flags */
+                               type_pf_data_flags(ahash_data(n, i),
+                                                  cadt_flags);
+#endif
                        ret = -IPSET_ERR_EXIST;
                        goto out;
                }
        TUNE_AHASH_MAX(h, multi);
-       ret = type_pf_elem_add(n, value, AHASH_MAX(h));
+       ret = type_pf_elem_add(n, value, AHASH_MAX(h), cadt_flags);
        if (ret != 0) {
                if (ret == -EAGAIN)
                        type_pf_data_next(h, d);
@@ -435,7 +462,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
        }
 
 #ifdef IP_SET_HASH_WITH_NETS
-       add_cidr(h, d->cidr, HOST_MASK);
+       add_cidr(h, CIDR(d->cidr), HOST_MASK);
 #endif
        h->elements++;
 out:
@@ -470,7 +497,7 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
                n->pos--;
                h->elements--;
 #ifdef IP_SET_HASH_WITH_NETS
-               del_cidr(h, d->cidr, HOST_MASK);
+               del_cidr(h, CIDR(d->cidr), HOST_MASK);
 #endif
                if (n->pos + AHASH_INIT_SIZE < n->size) {
                        void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
@@ -513,7 +540,7 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
                for (i = 0; i < n->pos; i++) {
                        data = ahash_data(n, i);
                        if (type_pf_data_equal(data, d, &multi))
-                               return 1;
+                               return type_pf_data_match(data);
                }
        }
        return 0;
@@ -535,7 +562,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
 #ifdef IP_SET_HASH_WITH_NETS
        /* If we test an IP address and not a network address,
         * try all possible network sizes */
-       if (d->cidr == SET_HOST_MASK(set->family))
+       if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
                return type_pf_test_cidrs(set, d, timeout);
 #endif
 
@@ -544,7 +571,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
        for (i = 0; i < n->pos; i++) {
                data = ahash_data(n, i);
                if (type_pf_data_equal(data, d, &multi))
-                       return 1;
+                       return type_pf_data_match(data);
        }
        return 0;
 }
@@ -700,7 +727,7 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
 
 static int
 type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value,
-                 u8 ahash_max, u32 timeout)
+                 u8 ahash_max, u32 cadt_flags, u32 timeout)
 {
        struct type_pf_elem *data;
 
@@ -727,6 +754,11 @@ type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value,
        data = ahash_tdata(n, n->pos++);
        type_pf_data_copy(data, value);
        type_pf_data_timeout_set(data, timeout);
+#ifdef IP_SET_HASH_WITH_NETS
+       /* Resizing won't overwrite stored flags */
+       if (cadt_flags)
+               type_pf_data_flags(data, cadt_flags);
+#endif
        return 0;
 }
 
@@ -747,7 +779,7 @@ type_pf_expire(struct ip_set_hash *h)
                        if (type_pf_data_expired(data)) {
                                pr_debug("expired %u/%u\n", i, j);
 #ifdef IP_SET_HASH_WITH_NETS
-                               del_cidr(h, data->cidr, HOST_MASK);
+                               del_cidr(h, CIDR(data->cidr), HOST_MASK);
 #endif
                                if (j != n->pos - 1)
                                        /* Not last one */
@@ -815,7 +847,7 @@ retry:
                for (j = 0; j < n->pos; j++) {
                        data = ahash_tdata(n, j);
                        m = hbucket(t, HKEY(data, h->initval, htable_bits));
-                       ret = type_pf_elem_tadd(m, data, AHASH_MAX(h),
+                       ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), 0,
                                                type_pf_data_timeout(data));
                        if (ret < 0) {
                                read_unlock_bh(&set->lock);
@@ -849,6 +881,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
        int ret = 0, i, j = AHASH_MAX(h) + 1;
        bool flag_exist = flags & IPSET_FLAG_EXIST;
        u32 key, multi = 0;
+       u32 cadt_flags = flags >> 16;
 
        if (h->elements >= h->maxelem)
                /* FIXME: when set is full, we slow down here */
@@ -868,6 +901,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
                data = ahash_tdata(n, i);
                if (type_pf_data_equal(data, d, &multi)) {
                        if (type_pf_data_expired(data) || flag_exist)
+                               /* Just timeout value may be updated */
                                j = i;
                        else {
                                ret = -IPSET_ERR_EXIST;
@@ -880,15 +914,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
        if (j != AHASH_MAX(h) + 1) {
                data = ahash_tdata(n, j);
 #ifdef IP_SET_HASH_WITH_NETS
-               del_cidr(h, data->cidr, HOST_MASK);
-               add_cidr(h, d->cidr, HOST_MASK);
+               del_cidr(h, CIDR(data->cidr), HOST_MASK);
+               add_cidr(h, CIDR(d->cidr), HOST_MASK);
 #endif
                type_pf_data_copy(data, d);
                type_pf_data_timeout_set(data, timeout);
+#ifdef IP_SET_HASH_WITH_NETS
+               type_pf_data_flags(data, cadt_flags);
+#endif
                goto out;
        }
        TUNE_AHASH_MAX(h, multi);
-       ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), timeout);
+       ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), cadt_flags, timeout);
        if (ret != 0) {
                if (ret == -EAGAIN)
                        type_pf_data_next(h, d);
@@ -896,7 +933,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
        }
 
 #ifdef IP_SET_HASH_WITH_NETS
-       add_cidr(h, d->cidr, HOST_MASK);
+       add_cidr(h, CIDR(d->cidr), HOST_MASK);
 #endif
        h->elements++;
 out:
@@ -930,7 +967,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
                n->pos--;
                h->elements--;
 #ifdef IP_SET_HASH_WITH_NETS
-               del_cidr(h, d->cidr, HOST_MASK);
+               del_cidr(h, CIDR(d->cidr), HOST_MASK);
 #endif
                if (n->pos + AHASH_INIT_SIZE < n->size) {
                        void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
@@ -968,8 +1005,9 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
                n = hbucket(t, key);
                for (i = 0; i < n->pos; i++) {
                        data = ahash_tdata(n, i);
-                       if (type_pf_data_equal(data, d, &multi))
-                               return !type_pf_data_expired(data);
+                       if (type_pf_data_equal(data, d, &multi) &&
+                           !type_pf_data_expired(data))
+                               return type_pf_data_match(data);
                }
        }
        return 0;
@@ -987,15 +1025,16 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
        u32 key, multi = 0;
 
 #ifdef IP_SET_HASH_WITH_NETS
-       if (d->cidr == SET_HOST_MASK(set->family))
+       if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
                return type_pf_ttest_cidrs(set, d, timeout);
 #endif
        key = HKEY(d, h->initval, t->htable_bits);
        n = hbucket(t, key);
        for (i = 0; i < n->pos; i++) {
                data = ahash_tdata(n, i);
-               if (type_pf_data_equal(data, d, &multi))
-                       return !type_pf_data_expired(data);
+               if (type_pf_data_equal(data, d, &multi) &&
+                   !type_pf_data_expired(data))
+                       return type_pf_data_match(data);
        }
        return 0;
 }
@@ -1108,14 +1147,17 @@ type_pf_gc_init(struct ip_set *set)
 #undef type_pf_data_isnull
 #undef type_pf_data_copy
 #undef type_pf_data_zero_out
+#undef type_pf_data_netmask
 #undef type_pf_data_list
 #undef type_pf_data_tlist
+#undef type_pf_data_next
+#undef type_pf_data_flags
+#undef type_pf_data_match
 
 #undef type_pf_elem
 #undef type_pf_telem
 #undef type_pf_data_timeout
 #undef type_pf_data_expired
-#undef type_pf_data_netmask
 #undef type_pf_data_timeout_set
 
 #undef type_pf_elem_add
@@ -1125,6 +1167,7 @@ type_pf_gc_init(struct ip_set *set)
 #undef type_pf_test
 
 #undef type_pf_elem_tadd
+#undef type_pf_del_telem
 #undef type_pf_expire
 #undef type_pf_tadd
 #undef type_pf_tdel
index 0edb35b81e36fb2da167f4921aa56c1bf299442e..5d05e69698626570ca8247732d4bb8f8af632674 100644 (file)
@@ -41,12 +41,19 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
 
 /* The type variant functions: IPv4 */
 
+/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
+ * However this way we have to store internally cidr - 1,
+ * dancing back and forth.
+ */
+#define IP_SET_HASH_WITH_NETS_PACKED
+
 /* Member elements without timeout */
 struct hash_ipportnet4_elem {
        __be32 ip;
        __be32 ip2;
        __be16 port;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        u8 proto;
 };
 
@@ -55,7 +62,8 @@ struct hash_ipportnet4_telem {
        __be32 ip;
        __be32 ip2;
        __be16 port;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        u8 proto;
        unsigned long timeout;
 };
@@ -85,11 +93,23 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
        memcpy(dst, src, sizeof(*dst));
 }
 
+static inline void
+hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags)
+{
+       dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem)
+{
+       return !elem->nomatch;
+}
+
 static inline void
 hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
 {
        elem->ip2 &= ip_set_netmask(cidr);
-       elem->cidr = cidr;
+       elem->cidr = cidr - 1;
 }
 
 static inline void
@@ -102,11 +122,15 @@ static bool
 hash_ipportnet4_data_list(struct sk_buff *skb,
                          const struct hash_ipportnet4_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -119,14 +143,17 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb,
 {
        const struct hash_ipportnet4_telem *tdata =
                (const struct hash_ipportnet4_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(tdata->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
 
        return 0;
 
@@ -158,13 +185,11 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet4_elem data = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
        };
 
-       if (data.cidr == 0)
-               return -EINVAL;
        if (adt == IPSET_TEST)
-               data.cidr = HOST_MASK;
+               data.cidr = HOST_MASK - 1;
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
@@ -172,7 +197,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
        ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
-       data.ip2 &= ip_set_netmask(data.cidr);
+       data.ip2 &= ip_set_netmask(data.cidr + 1);
 
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
@@ -183,17 +208,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
+       struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 };
        u32 ip, ip_to = 0, p = 0, port, port_to;
        u32 ip2_from = 0, ip2_to, ip2_last, ip2;
        u32 timeout = h->timeout;
        bool with_ports = false;
+       u8 cidr;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
                return -IPSET_ERR_PROTOCOL;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -208,9 +235,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                return ret;
 
        if (tb[IPSET_ATTR_CIDR2]) {
-               data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
-               if (!data.cidr)
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr || cidr > HOST_MASK)
                        return -IPSET_ERR_INVALID_CIDR;
+               data.cidr = cidr - 1;
        }
 
        if (tb[IPSET_ATTR_PORT])
@@ -236,12 +264,18 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
        if (adt == IPSET_TEST ||
            !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
              tb[IPSET_ATTR_IP2_TO])) {
                data.ip = htonl(ip);
-               data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr));
+               data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr + 1));
                ret = adtfn(set, &data, timeout, flags);
                return ip_set_eexist(ret, flags) ? 0 : ret;
        }
@@ -275,7 +309,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                if (ip2_from + UINT_MAX == ip2_to)
                        return -IPSET_ERR_HASH_RANGE;
        } else {
-               ip_set_mask_from_to(ip2_from, ip2_to, data.cidr);
+               ip_set_mask_from_to(ip2_from, ip2_to, data.cidr + 1);
        }
 
        if (retried)
@@ -290,7 +324,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                        while (!after(ip2, ip2_to)) {
                                data.ip2 = htonl(ip2);
                                ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
-                                                               &data.cidr);
+                                                               &cidr);
+                               data.cidr = cidr - 1;
                                ret = adtfn(set, &data, timeout, flags);
 
                                if (ret && !ip_set_eexist(ret, flags))
@@ -321,7 +356,8 @@ struct hash_ipportnet6_elem {
        union nf_inet_addr ip;
        union nf_inet_addr ip2;
        __be16 port;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        u8 proto;
 };
 
@@ -329,7 +365,8 @@ struct hash_ipportnet6_telem {
        union nf_inet_addr ip;
        union nf_inet_addr ip2;
        __be16 port;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        u8 proto;
        unsigned long timeout;
 };
@@ -359,6 +396,18 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
        memcpy(dst, src, sizeof(*dst));
 }
 
+static inline void
+hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags)
+{
+       dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem)
+{
+       return !elem->nomatch;
+}
+
 static inline void
 hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
 {
@@ -378,18 +427,22 @@ static inline void
 hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
 {
        ip6_netmask(&elem->ip2, cidr);
-       elem->cidr = cidr;
+       elem->cidr = cidr - 1;
 }
 
 static bool
 hash_ipportnet6_data_list(struct sk_buff *skb,
                          const struct hash_ipportnet6_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -402,14 +455,17 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb,
 {
        const struct hash_ipportnet6_telem *e =
                (const struct hash_ipportnet6_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(e->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -438,13 +494,11 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportnet6_elem data = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
        };
 
-       if (data.cidr == 0)
-               return -EINVAL;
        if (adt == IPSET_TEST)
-               data.cidr = HOST_MASK;
+               data.cidr = HOST_MASK - 1;
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
@@ -452,7 +506,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
        ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
-       ip6_netmask(&data.ip2, data.cidr);
+       ip6_netmask(&data.ip2, data.cidr + 1);
 
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
@@ -463,16 +517,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
+       struct hash_ipportnet6_elem data = { .cidr = HOST_MASK - 1 };
        u32 port, port_to;
        u32 timeout = h->timeout;
        bool with_ports = false;
+       u8 cidr;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
                     tb[IPSET_ATTR_IP_TO] ||
                     tb[IPSET_ATTR_CIDR]))
                return -IPSET_ERR_PROTOCOL;
@@ -490,13 +546,14 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
        if (ret)
                return ret;
 
-       if (tb[IPSET_ATTR_CIDR2])
-               data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
-
-       if (!data.cidr)
-               return -IPSET_ERR_INVALID_CIDR;
+       if (tb[IPSET_ATTR_CIDR2]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               data.cidr = cidr - 1;
+       }
 
-       ip6_netmask(&data.ip2, data.cidr);
+       ip6_netmask(&data.ip2, data.cidr + 1);
 
        if (tb[IPSET_ATTR_PORT])
                data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -521,6 +578,12 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
                ret = adtfn(set, &data, timeout, flags);
                return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -624,7 +687,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
        .family         = NFPROTO_UNSPEC,
        .revision_min   = 0,
        /*                1        SCTP and UDPLITE support added */
-       .revision_max   = 2,    /* Range as input support for IPv4 added */
+       /*                2        Range as input support for IPv4 added */
+       .revision_max   = 3,    /* nomatch flag support added */
        .create         = hash_ipportnet_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -643,6 +707,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
                [IPSET_ATTR_CIDR2]      = { .type = NLA_U8 },
                [IPSET_ATTR_PROTO]      = { .type = NLA_U8 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
        },
index 48e35ba99a3567515dc50387edf600d1ef658863..eb8e6d4d66e1dfa8c608b2499ee1bc96b908dc94 100644 (file)
@@ -43,7 +43,7 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
 struct hash_net4_elem {
        __be32 ip;
        u16 padding0;
-       u8 padding1;
+       u8 nomatch;
        u8 cidr;
 };
 
@@ -51,7 +51,7 @@ struct hash_net4_elem {
 struct hash_net4_telem {
        __be32 ip;
        u16 padding0;
-       u8 padding1;
+       u8 nomatch;
        u8 cidr;
        unsigned long timeout;
 };
@@ -61,7 +61,8 @@ hash_net4_data_equal(const struct hash_net4_elem *ip1,
                     const struct hash_net4_elem *ip2,
                     u32 *multi)
 {
-       return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
+       return ip1->ip == ip2->ip &&
+              ip1->cidr == ip2->cidr;
 }
 
 static inline bool
@@ -76,6 +77,19 @@ hash_net4_data_copy(struct hash_net4_elem *dst,
 {
        dst->ip = src->ip;
        dst->cidr = src->cidr;
+       dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags)
+{
+       dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_net4_data_match(const struct hash_net4_elem *elem)
+{
+       return !elem->nomatch;
 }
 
 static inline void
@@ -95,8 +109,12 @@ hash_net4_data_zero_out(struct hash_net4_elem *elem)
 static bool
 hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -108,11 +126,14 @@ hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
 {
        const struct hash_net4_telem *tdata =
                (const struct hash_net4_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(tdata->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
 
        return 0;
 
@@ -167,7 +188,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
-                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
                return -IPSET_ERR_PROTOCOL;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -179,7 +201,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        if (tb[IPSET_ATTR_CIDR]) {
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-               if (!data.cidr)
+               if (!data.cidr || data.cidr > HOST_MASK)
                        return -IPSET_ERR_INVALID_CIDR;
        }
 
@@ -189,6 +211,12 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
                data.ip = htonl(ip & ip_set_hostmask(data.cidr));
                ret = adtfn(set, &data, timeout, flags);
@@ -236,14 +264,14 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
 struct hash_net6_elem {
        union nf_inet_addr ip;
        u16 padding0;
-       u8 padding1;
+       u8 nomatch;
        u8 cidr;
 };
 
 struct hash_net6_telem {
        union nf_inet_addr ip;
        u16 padding0;
-       u8 padding1;
+       u8 nomatch;
        u8 cidr;
        unsigned long timeout;
 };
@@ -269,6 +297,19 @@ hash_net6_data_copy(struct hash_net6_elem *dst,
 {
        ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
        dst->cidr = src->cidr;
+       dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags)
+{
+       dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_net6_data_match(const struct hash_net6_elem *elem)
+{
+       return !elem->nomatch;
 }
 
 static inline void
@@ -296,8 +337,12 @@ hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
 static bool
 hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -309,11 +354,14 @@ hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
 {
        const struct hash_net6_telem *e =
                (const struct hash_net6_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(e->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -366,7 +414,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
-                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
                return -IPSET_ERR_PROTOCOL;
        if (unlikely(tb[IPSET_ATTR_IP_TO]))
                return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -381,7 +430,7 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
        if (tb[IPSET_ATTR_CIDR])
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
-       if (!data.cidr)
+       if (!data.cidr || data.cidr > HOST_MASK)
                return -IPSET_ERR_INVALID_CIDR;
 
        ip6_netmask(&data.ip, data.cidr);
@@ -392,6 +441,12 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        ret = adtfn(set, &data, timeout, flags);
 
        return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -474,7 +529,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
        .dimension      = IPSET_DIM_ONE,
        .family         = NFPROTO_UNSPEC,
        .revision_min   = 0,
-       .revision_max   = 1,    /* Range as input support for IPv4 added */
+       /*              = 1        Range as input support for IPv4 added */
+       .revision_max   = 2,    /* nomatch flag support added */
        .create         = hash_net_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -488,6 +544,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
                [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
        },
        .me             = THIS_MODULE,
 };
index a9fb4afafa8bac1c145b55d39f2ef78550e338ab..f24037ff432201015e81731f61133d45079128d6 100644 (file)
@@ -163,7 +163,8 @@ struct hash_netiface4_elem_hashed {
        __be32 ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
 };
 
 #define HKEY_DATALEN   sizeof(struct hash_netiface4_elem_hashed)
@@ -173,7 +174,8 @@ struct hash_netiface4_elem {
        __be32 ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
        const char *iface;
 };
 
@@ -182,7 +184,8 @@ struct hash_netiface4_telem {
        __be32 ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
        const char *iface;
        unsigned long timeout;
 };
@@ -207,11 +210,25 @@ hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem)
 
 static inline void
 hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
-                        const struct hash_netiface4_elem *src) {
+                        const struct hash_netiface4_elem *src)
+{
        dst->ip = src->ip;
        dst->cidr = src->cidr;
        dst->physdev = src->physdev;
        dst->iface = src->iface;
+       dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags)
+{
+       dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_netiface4_data_match(const struct hash_netiface4_elem *elem)
+{
+       return !elem->nomatch;
 }
 
 static inline void
@@ -233,11 +250,13 @@ hash_netiface4_data_list(struct sk_buff *skb,
 {
        u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
 
+       if (data->nomatch)
+               flags |= IPSET_FLAG_NOMATCH;
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
        NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
        if (flags)
-               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -252,11 +271,13 @@ hash_netiface4_data_tlist(struct sk_buff *skb,
                (const struct hash_netiface4_telem *)data;
        u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
 
+       if (data->nomatch)
+               flags |= IPSET_FLAG_NOMATCH;
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
        NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
        if (flags)
-               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(tdata->timeout)));
 
@@ -361,7 +382,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        if (tb[IPSET_ATTR_CIDR]) {
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-               if (!data.cidr)
+               if (!data.cidr || data.cidr > HOST_MASK)
                        return -IPSET_ERR_INVALID_CIDR;
        }
 
@@ -387,6 +408,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
                u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
                if (cadt_flags & IPSET_FLAG_PHYSDEV)
                        data.physdev = 1;
+               if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
+                       flags |= (cadt_flags << 16);
        }
 
        if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
@@ -440,7 +463,8 @@ struct hash_netiface6_elem_hashed {
        union nf_inet_addr ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
 };
 
 #define HKEY_DATALEN   sizeof(struct hash_netiface6_elem_hashed)
@@ -449,7 +473,8 @@ struct hash_netiface6_elem {
        union nf_inet_addr ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
        const char *iface;
 };
 
@@ -457,7 +482,8 @@ struct hash_netiface6_telem {
        union nf_inet_addr ip;
        u8 physdev;
        u8 cidr;
-       u16 padding;
+       u8 nomatch;
+       u8 padding;
        const char *iface;
        unsigned long timeout;
 };
@@ -487,9 +513,22 @@ hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
        memcpy(dst, src, sizeof(*dst));
 }
 
+static inline void
+hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags)
+{
+       dst->nomatch = flags & IPSET_FLAG_NOMATCH;
+}
+
+static inline bool
+hash_netiface6_data_match(const struct hash_netiface6_elem *elem)
+{
+       return !elem->nomatch;
+}
+
 static inline void
 hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
 {
+       elem->cidr = 0;
 }
 
 static inline void
@@ -514,11 +553,13 @@ hash_netiface6_data_list(struct sk_buff *skb,
 {
        u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
 
+       if (data->nomatch)
+               flags |= IPSET_FLAG_NOMATCH;
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
        NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
        if (flags)
-               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -533,11 +574,13 @@ hash_netiface6_data_tlist(struct sk_buff *skb,
                (const struct hash_netiface6_telem *)data;
        u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
 
+       if (data->nomatch)
+               flags |= IPSET_FLAG_NOMATCH;
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
        NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
        NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface);
        if (flags)
-               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags);
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(e->timeout)));
        return 0;
@@ -636,7 +679,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 
        if (tb[IPSET_ATTR_CIDR])
                data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-       if (!data.cidr)
+       if (!data.cidr || data.cidr > HOST_MASK)
                return -IPSET_ERR_INVALID_CIDR;
        ip6_netmask(&data.ip, data.cidr);
 
@@ -662,6 +705,8 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
                u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
                if (cadt_flags & IPSET_FLAG_PHYSDEV)
                        data.physdev = 1;
+               if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
+                       flags |= (cadt_flags << 16);
        }
 
        ret = adtfn(set, &data, timeout, flags);
@@ -748,6 +793,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
        .dimension      = IPSET_DIM_TWO,
        .family         = NFPROTO_UNSPEC,
        .revision_min   = 0,
+       .revision_max   = 1,    /* nomatch flag support added */
        .create         = hash_netiface_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
index 1fcc1020eaae3de5139d2c04178465758e57972d..ce2e77100b64ecb521db1cf45c0babd4902a374f 100644 (file)
@@ -40,12 +40,19 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
 
 /* The type variant functions: IPv4 */
 
+/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
+ * However this way we have to store internally cidr - 1,
+ * dancing back and forth.
+ */
+#define IP_SET_HASH_WITH_NETS_PACKED
+
 /* Member elements without timeout */
 struct hash_netport4_elem {
        __be32 ip;
        __be16 port;
        u8 proto;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
 };
 
 /* Member elements with timeout support */
@@ -53,7 +60,8 @@ struct hash_netport4_telem {
        __be32 ip;
        __be16 port;
        u8 proto;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        unsigned long timeout;
 };
 
@@ -82,13 +90,26 @@ hash_netport4_data_copy(struct hash_netport4_elem *dst,
        dst->port = src->port;
        dst->proto = src->proto;
        dst->cidr = src->cidr;
+       dst->nomatch = src->nomatch;
+}
+
+static inline void
+hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
+{
+       dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_netport4_data_match(const struct hash_netport4_elem *elem)
+{
+       return !elem->nomatch;
 }
 
 static inline void
 hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
 {
        elem->ip &= ip_set_netmask(cidr);
-       elem->cidr = cidr;
+       elem->cidr = cidr - 1;
 }
 
 static inline void
@@ -101,10 +122,14 @@ static bool
 hash_netport4_data_list(struct sk_buff *skb,
                        const struct hash_netport4_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -117,13 +142,16 @@ hash_netport4_data_tlist(struct sk_buff *skb,
 {
        const struct hash_netport4_telem *tdata =
                (const struct hash_netport4_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(tdata->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
 
        return 0;
 
@@ -154,20 +182,18 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem data = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
        };
 
-       if (data.cidr == 0)
-               return -EINVAL;
        if (adt == IPSET_TEST)
-               data.cidr = HOST_MASK;
+               data.cidr = HOST_MASK - 1;
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
                return -EINVAL;
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-       data.ip &= ip_set_netmask(data.cidr);
+       data.ip &= ip_set_netmask(data.cidr + 1);
 
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
@@ -178,16 +204,18 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       struct hash_netport4_elem data = { .cidr = HOST_MASK };
+       struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 };
        u32 port, port_to, p = 0, ip = 0, ip_to, last;
        u32 timeout = h->timeout;
        bool with_ports = false;
+       u8 cidr;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
                return -IPSET_ERR_PROTOCOL;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -198,9 +226,10 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
                return ret;
 
        if (tb[IPSET_ATTR_CIDR]) {
-               data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-               if (!data.cidr)
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
                        return -IPSET_ERR_INVALID_CIDR;
+               data.cidr = cidr - 1;
        }
 
        if (tb[IPSET_ATTR_PORT])
@@ -227,8 +256,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        }
 
        with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
-               data.ip = htonl(ip & ip_set_hostmask(data.cidr));
+               data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
                ret = adtfn(set, &data, timeout, flags);
                return ip_set_eexist(ret, flags) ? 0 : ret;
        }
@@ -248,14 +284,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
                if (ip + UINT_MAX == ip_to)
                        return -IPSET_ERR_HASH_RANGE;
        } else {
-               ip_set_mask_from_to(ip, ip_to, data.cidr);
+               ip_set_mask_from_to(ip, ip_to, data.cidr + 1);
        }
 
        if (retried)
                ip = h->next.ip;
        while (!after(ip, ip_to)) {
                data.ip = htonl(ip);
-               last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
+               last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               data.cidr = cidr - 1;
                p = retried && ip == h->next.ip ? h->next.port : port;
                for (; p <= port_to; p++) {
                        data.port = htons(p);
@@ -288,14 +325,16 @@ struct hash_netport6_elem {
        union nf_inet_addr ip;
        __be16 port;
        u8 proto;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
 };
 
 struct hash_netport6_telem {
        union nf_inet_addr ip;
        __be16 port;
        u8 proto;
-       u8 cidr;
+       u8 cidr:7;
+       u8 nomatch:1;
        unsigned long timeout;
 };
 
@@ -323,6 +362,18 @@ hash_netport6_data_copy(struct hash_netport6_elem *dst,
        memcpy(dst, src, sizeof(*dst));
 }
 
+static inline void
+hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
+{
+       dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+}
+
+static inline bool
+hash_netport6_data_match(const struct hash_netport6_elem *elem)
+{
+       return !elem->nomatch;
+}
+
 static inline void
 hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
 {
@@ -342,17 +393,21 @@ static inline void
 hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
 {
        ip6_netmask(&elem->ip, cidr);
-       elem->cidr = cidr;
+       elem->cidr = cidr - 1;
 }
 
 static bool
 hash_netport6_data_list(struct sk_buff *skb,
                        const struct hash_netport6_elem *data)
 {
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
+
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -365,13 +420,16 @@ hash_netport6_data_tlist(struct sk_buff *skb,
 {
        const struct hash_netport6_telem *e =
                (const struct hash_netport6_telem *)data;
+       u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
 
        NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip);
        NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
-       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr);
+       NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1);
        NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
        NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
                      htonl(ip_set_timeout_get(e->timeout)));
+       if (flags)
+               NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags));
        return 0;
 
 nla_put_failure:
@@ -400,20 +458,18 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport6_elem data = {
-               .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
+               .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
        };
 
-       if (data.cidr == 0)
-               return -EINVAL;
        if (adt == IPSET_TEST)
-               data.cidr = HOST_MASK;
+               data.cidr = HOST_MASK - 1;
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
                return -EINVAL;
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-       ip6_netmask(&data.ip, data.cidr);
+       ip6_netmask(&data.ip, data.cidr + 1);
 
        return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
@@ -424,16 +480,18 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
-       struct hash_netport6_elem data = { .cidr = HOST_MASK };
+       struct hash_netport6_elem data = { .cidr = HOST_MASK  - 1 };
        u32 port, port_to;
        u32 timeout = h->timeout;
        bool with_ports = false;
+       u8 cidr;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] ||
                     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
                     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+                    !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
                return -IPSET_ERR_PROTOCOL;
        if (unlikely(tb[IPSET_ATTR_IP_TO]))
                return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -445,11 +503,13 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
        if (ret)
                return ret;
 
-       if (tb[IPSET_ATTR_CIDR])
-               data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-       if (!data.cidr)
-               return -IPSET_ERR_INVALID_CIDR;
-       ip6_netmask(&data.ip, data.cidr);
+       if (tb[IPSET_ATTR_CIDR]) {
+               cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+               if (!cidr || cidr > HOST_MASK)
+                       return -IPSET_ERR_INVALID_CIDR;
+               data.cidr = cidr - 1;
+       }
+       ip6_netmask(&data.ip, data.cidr + 1);
 
        if (tb[IPSET_ATTR_PORT])
                data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
@@ -474,6 +534,12 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
+       if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+               u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+               if (cadt_flags & IPSET_FLAG_NOMATCH)
+                       flags |= (cadt_flags << 16);
+       }
+
        if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
                ret = adtfn(set, &data, timeout, flags);
                return ip_set_eexist(ret, flags) ? 0 : ret;
@@ -576,7 +642,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
        .family         = NFPROTO_UNSPEC,
        .revision_min   = 0,
        /*                1        SCTP and UDPLITE support added */
-       .revision_max   = 2,    /* Range as input support for IPv4 added */
+       /*                2,       Range as input support for IPv4 added */
+       .revision_max   = 3,    /* nomatch flag support added */
        .create         = hash_netport_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
@@ -595,6 +662,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
+               [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
        },
        .me             = THIS_MODULE,
 };
index 0210b7ba90eb9e0941936f0ba162605f90e86017..3bbb75b78f0406c4939a86aa0324bb1f45c1cecf 100644 (file)
@@ -319,11 +319,20 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
        case IPSET_OPT_PHYSDEV:
                cadt_flag_type_attr(data, opt, IPSET_FLAG_PHYSDEV);
                break;
+       case IPSET_OPT_NOMATCH:
+               cadt_flag_type_attr(data, opt, IPSET_FLAG_NOMATCH);
+               break;
        case IPSET_OPT_FLAGS:
                data->flags = *(const uint32_t *)value;
                break;
        case IPSET_OPT_CADT_FLAGS:
                data->cadt_flags = *(const uint32_t *)value;
+               if (data->cadt_flags & IPSET_FLAG_BEFORE)
+                       ipset_data_flags_set(data, IPSET_FLAG(IPSET_OPT_BEFORE));
+               if (data->cadt_flags & IPSET_FLAG_PHYSDEV)
+                       ipset_data_flags_set(data, IPSET_FLAG(IPSET_OPT_PHYSDEV));
+               if (data->cadt_flags & IPSET_FLAG_NOMATCH)
+                       ipset_data_flags_set(data, IPSET_FLAG(IPSET_OPT_NOMATCH));
                break;
        default:
                return -1;
@@ -432,6 +441,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
        case IPSET_OPT_CADT_FLAGS:
        case IPSET_OPT_BEFORE:
        case IPSET_OPT_PHYSDEV:
+       case IPSET_OPT_NOMATCH:
                return &data->cadt_flags;
        default:
                return NULL;
@@ -485,6 +495,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
        /* Flags doesn't counted once :-( */
        case IPSET_OPT_BEFORE:
        case IPSET_OPT_PHYSDEV:
+       case IPSET_OPT_NOMATCH:
                return sizeof(uint32_t);
        default:
                return 0;
index c4cf97e0604db06720df71572124ea7d07eaa463..0813b7dd71d8f4f08d29f8b05b57feb7cb5cd9d8 100644 (file)
@@ -252,3 +252,108 @@ struct ipset_type ipset_hash_ipportnet2 = {
        .usagefn = ipset_port_usage,
 };
 
+static const struct ipset_arg hash_ipportnet3_add_args[] = {
+       { .name = { "timeout", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .opt = IPSET_OPT_TIMEOUT,
+         .parse = ipset_parse_uint32,          .print = ipset_print_number,
+       },
+       { .name = { "nomatch", NULL },
+         .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_NOMATCH,
+         .parse = ipset_parse_flag,            .print = ipset_print_flag,
+       },
+       { },
+};
+
+static const char hash_ipportnet3_usage[] =
+"create SETNAME hash:ip,port,net\n"
+"              [family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE]\n"
+"add    SETNAME IP,PROTO:PORT,IP[/CIDR] [timeout VALUE] [nomatch]\n"
+"del    SETNAME IP,PROTO:PORT,IP[/CIDR]\n"
+"test   SETNAME IP,PROTO:PORT,IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP are valid IPv4 or IPv6 addresses (or hostnames),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      in both IP components are supported for IPv4.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+struct ipset_type ipset_hash_ipportnet3 = {
+       .name = "hash:ip,port,net",
+       .alias = { "ipportnethash", NULL },
+       .revision = 3,
+       .family = NFPROTO_IPSET_IPV46,
+       .dimension = IPSET_DIM_THREE,
+       .elem = {
+               [IPSET_DIM_ONE - 1] = {
+                       .parse = ipset_parse_ip4_single6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP
+               },
+               [IPSET_DIM_TWO - 1] = {
+                       .parse = ipset_parse_proto_port,
+                       .print = ipset_print_proto_port,
+                       .opt = IPSET_OPT_PORT
+               },
+               [IPSET_DIM_THREE - 1] = {
+                       .parse = ipset_parse_ip4_net6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP2
+               },
+       },
+       .args = {
+               [IPSET_CREATE] = hash_ipportnet_create_args,
+               [IPSET_ADD] = hash_ipportnet3_add_args,
+       },
+       .mandatory = {
+               [IPSET_CREATE] = 0,
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2),
+       },
+       .full = {
+               [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+                       | IPSET_FLAG(IPSET_OPT_MAXELEM)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PORT_TO)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2)
+                       | IPSET_FLAG(IPSET_OPT_CIDR2)
+                       | IPSET_FLAG(IPSET_OPT_IP2_TO)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT)
+                       | IPSET_FLAG(IPSET_OPT_NOMATCH),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PORT_TO)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2)
+                       | IPSET_FLAG(IPSET_OPT_CIDR2)
+                       | IPSET_FLAG(IPSET_OPT_IP2_TO),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_IP2)
+                       | IPSET_FLAG(IPSET_OPT_CIDR2),
+       },
+
+       .usage = hash_ipportnet3_usage,
+       .usagefn = ipset_port_usage,
+};
+
index 76269f08b1f594a0fa25291e5ff157642c23599c..0fd260816bd9026774a996d5943bd914e5e6116e 100644 (file)
@@ -162,3 +162,70 @@ struct ipset_type ipset_hash_net1 = {
        .usage = hash_net1_usage,
 };
 
+static const struct ipset_arg hash_net2_add_args[] = {
+       { .name = { "timeout", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .opt = IPSET_OPT_TIMEOUT,
+         .parse = ipset_parse_uint32,          .print = ipset_print_number,
+       },
+       { .name = { "nomatch", NULL },
+         .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_NOMATCH,
+         .parse = ipset_parse_flag,            .print = ipset_print_flag,
+       },
+       { },
+};
+
+static const char hash_net2_usage[] =
+"create SETNAME hash:net\n"
+"              [family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO [timeout VALUE] [nomatch]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO\n"
+"test   SETNAME IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP is an IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      IP range is not supported with IPv6.\n";
+
+struct ipset_type ipset_hash_net2 = {
+       .name = "hash:net",
+       .alias = { "nethash", NULL },
+       .revision = 2,
+       .family = NFPROTO_IPSET_IPV46,
+       .dimension = IPSET_DIM_ONE,
+       .elem = {
+               [IPSET_DIM_ONE - 1] = {
+                       .parse = ipset_parse_ip4_net6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP
+               },
+       },
+       .args = {
+               [IPSET_CREATE] = hash_net_create_args,
+               [IPSET_ADD] = hash_net2_add_args,
+       },
+       .mandatory = {
+               [IPSET_CREATE] = 0,
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
+       },
+       .full = {
+               [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+                       | IPSET_FLAG(IPSET_OPT_MAXELEM)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT)
+                       | IPSET_FLAG(IPSET_OPT_NOMATCH),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR),
+       },
+
+       .usage = hash_net2_usage,
+};
+
index 51d9cad0ff39ca7a1379a6773c63e3604c4b2a37..880ba7def3bae5910d76532152b10670311d1b71 100644 (file)
@@ -118,3 +118,85 @@ struct ipset_type ipset_hash_netiface0 = {
        .usage = hash_netiface_usage,
 };
 
+static const struct ipset_arg hash_netiface1_add_args[] = {
+       { .name = { "timeout", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .opt = IPSET_OPT_TIMEOUT,
+         .parse = ipset_parse_uint32,          .print = ipset_print_number,
+       },
+       { .name = { "nomatch", NULL },
+         .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_NOMATCH,
+         .parse = ipset_parse_flag,            .print = ipset_print_flag,
+       },
+       { },
+};
+
+static const char hash_netiface1_usage[] =
+"create SETNAME hash:net,iface\n"
+"              [family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE [timeout VALUE] [nomatch]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE\n"
+"test   SETNAME IP[/CIDR],[physdev:]IFACE\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements with IPv4 is supported.\n";
+
+struct ipset_type ipset_hash_netiface1 = {
+       .name = "hash:net,iface",
+       .alias = { "netifacehash", NULL },
+       .revision = 1,
+       .family = NFPROTO_IPSET_IPV46,
+       .dimension = IPSET_DIM_TWO,
+       .elem = {
+               [IPSET_DIM_ONE - 1] = {
+                       .parse = ipset_parse_ip4_net6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP
+               },
+               [IPSET_DIM_TWO - 1] = {
+                       .parse = ipset_parse_iface,
+                       .print = ipset_print_iface,
+                       .opt = IPSET_OPT_IFACE
+               },
+       },
+       .args = {
+               [IPSET_CREATE] = hash_netiface_create_args,
+               [IPSET_ADD] = hash_netiface1_add_args,
+       },
+       .mandatory = {
+               [IPSET_CREATE] = 0,
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_IFACE),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_IFACE),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_IFACE),
+       },
+       .full = {
+               [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+                       | IPSET_FLAG(IPSET_OPT_MAXELEM)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_IFACE)
+                       | IPSET_FLAG(IPSET_OPT_PHYSDEV)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT)
+                       | IPSET_FLAG(IPSET_OPT_NOMATCH),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_IFACE)
+                       | IPSET_FLAG(IPSET_OPT_PHYSDEV),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_IFACE)
+                       | IPSET_FLAG(IPSET_OPT_PHYSDEV),
+       },
+
+       .usage = hash_netiface1_usage,
+};
+
index af6adf1409b4944d3840fc5e1cd090c4c8cbacd1..b910c81e24f8b4fb41bcfb2e6f92dd7415feee50 100644 (file)
@@ -197,3 +197,92 @@ struct ipset_type ipset_hash_netport2 = {
        .usage = hash_netport2_usage,
        .usagefn = ipset_port_usage,
 };
+
+static const struct ipset_arg hash_netport3_add_args[] = {
+       { .name = { "timeout", NULL },
+         .has_arg = IPSET_MANDATORY_ARG,       .opt = IPSET_OPT_TIMEOUT,
+         .parse = ipset_parse_uint32,          .print = ipset_print_number,
+       },
+       { .name = { "nomatch", NULL },
+         .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_NOMATCH,
+         .parse = ipset_parse_flag,            .print = ipset_print_flag,
+       },
+       { },
+};
+
+static const char hash_netport3_usage[] =
+"create SETNAME hash:net,port\n"
+"              [family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT [timeout VALUE] [nomatch]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT\n"
+"test   SETNAME IP[/CIDR],PROTO:PORT\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements with IPv4 is supported.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+struct ipset_type ipset_hash_netport3 = {
+       .name = "hash:net,port",
+       .alias = { "netporthash", NULL },
+       .revision = 3,
+       .family = NFPROTO_IPSET_IPV46,
+       .dimension = IPSET_DIM_TWO,
+       .elem = {
+               [IPSET_DIM_ONE - 1] = {
+                       .parse = ipset_parse_ip4_net6,
+                       .print = ipset_print_ip,
+                       .opt = IPSET_OPT_IP
+               },
+               [IPSET_DIM_TWO - 1] = {
+                       .parse = ipset_parse_proto_port,
+                       .print = ipset_print_proto_port,
+                       .opt = IPSET_OPT_PORT
+               },
+       },
+       .args = {
+               [IPSET_CREATE] = hash_netport_create_args,
+               [IPSET_ADD] = hash_netport3_add_args,
+       },
+       .mandatory = {
+               [IPSET_CREATE] = 0,
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_PORT),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_PORT),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_PORT),
+       },
+       .full = {
+               [IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+                       | IPSET_FLAG(IPSET_OPT_MAXELEM)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT),
+               [IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PORT_TO)
+                       | IPSET_FLAG(IPSET_OPT_PROTO)
+                       | IPSET_FLAG(IPSET_OPT_TIMEOUT)
+                       | IPSET_FLAG(IPSET_OPT_NOMATCH),
+               [IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_IP_TO)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PORT_TO)
+                       | IPSET_FLAG(IPSET_OPT_PROTO),
+               [IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+                       | IPSET_FLAG(IPSET_OPT_CIDR)
+                       | IPSET_FLAG(IPSET_OPT_PORT)
+                       | IPSET_FLAG(IPSET_OPT_PROTO),
+       },
+
+       .usage = hash_netport3_usage,
+       .usagefn = ipset_port_usage,
+};
index eeb253c7221b01d3c3b94bdfe373d068c34da0d5..0ee34c3aecbcdc5fe723fa0c523afc5bd44dd7d5 100644 (file)
@@ -1362,11 +1362,11 @@ ipset_parse_netmask(struct ipset_session *session,
  */
 int
 ipset_parse_flag(struct ipset_session *session,
-                enum ipset_opt opt, const char *str UNUSED)
+                enum ipset_opt opt, const char *str)
 {
        assert(session);
 
-       return ipset_session_data_set(session, opt, NULL);
+       return ipset_session_data_set(session, opt, str);
 }
 
 /**
index f04377fe278fa26297ea90804d700c7e2a019b8c..662baaef569620b274560dd088734f8445d8ebdc 100644 (file)
@@ -501,7 +501,7 @@ ipset_print_iface(char *buf, unsigned int len,
        }
        name = ipset_data_get(data, opt);
        assert(name);
-       size = snprintf(buf, len, "%s", name);
+       size = snprintf(buf + offset, len, "%s", name);
        SNPRINTF_FAILURE(size, len, offset);
        return offset;
 }
index 472b9747b230970cf5515909860fa140f9b94e64..2ff463ed207d2004c7326a5636e076b793108edd 100644 (file)
@@ -550,6 +550,8 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
        struct ipset_data *data = session->data;
        const struct ipset_attr_policy *attr;
        const void *d;
+       uint32_t v32;
+       uint16_t v16;
        int ret;
 
        attr = &attrs[type];
@@ -560,7 +562,7 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
                struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
                uint8_t family = ipset_data_family(data);
                int atype;
-               D("attr type %u", type);
+               D("IP attr type %u", type);
                if (mnl_attr_parse_nested(nla[type],
                                          ipaddr_attr_cb, ipattr) < 0)
                        FAILURE("Broken kernel message, cannot validate "
@@ -595,21 +597,16 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
                }
                d = mnl_attr_get_payload(ipattr[atype]);
        } else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
+               D("netorder attr type %u", type);
                switch (attr->type) {
                case MNL_TYPE_U32: {
-                       uint32_t value;
-
-                       value  = ntohl(*(const uint32_t *)d);
-
-                       d = &value;
+                       v32  = ntohl(*(const uint32_t *)d);
+                       d = &v32;
                        break;
                }
                case MNL_TYPE_U16: {
-                       uint16_t value;
-
-                       value = ntohs(*(const uint16_t *)d);
-
-                       d = &value;
+                       v16 = ntohs(*(const uint16_t *)d);
+                       d = &v16;
                        break;
                }
                default:
@@ -617,6 +614,8 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
                }
        }
 #ifdef IPSET_DEBUG
+        else
+               D("hostorder attr type %u", type);
        if (type == IPSET_ATTR_TYPENAME)
                D("nla typename %s", (const char *) d);
 #endif
index 0b5826e572fecd75f8d1d32c9f373b18e9649ed7..2c8e04f72d4d0913f42650bb46643847cfd290ca 100644 (file)
@@ -26,13 +26,17 @@ extern struct ipset_type ipset_bitmap_port0;
 extern struct ipset_type ipset_hash_ip0;
 extern struct ipset_type ipset_hash_net0;
 extern struct ipset_type ipset_hash_net1;
+extern struct ipset_type ipset_hash_net2;
 extern struct ipset_type ipset_hash_netport1;
 extern struct ipset_type ipset_hash_netport2;
+extern struct ipset_type ipset_hash_netport3;
 extern struct ipset_type ipset_hash_netiface0;
+extern struct ipset_type ipset_hash_netiface1;
 extern struct ipset_type ipset_hash_ipport1;
 extern struct ipset_type ipset_hash_ipportip1;
 extern struct ipset_type ipset_hash_ipportnet1;
 extern struct ipset_type ipset_hash_ipportnet2;
+extern struct ipset_type ipset_hash_ipportnet3;
 extern struct ipset_type ipset_list_set0;
 
 /* Userspace cache of sets which exists in the kernel */
@@ -588,13 +592,16 @@ ipset_cache_fini(void)
        ipset_type_add(&ipset_hash_ip0);
        ipset_type_add(&ipset_hash_net0);
        ipset_type_add(&ipset_hash_net1);
+       ipset_type_add(&ipset_hash_net2);
        ipset_type_add(&ipset_hash_netport1);
        ipset_type_add(&ipset_hash_netport2);
+       ipset_type_add(&ipset_hash_netport3);
        ipset_type_add(&ipset_hash_netiface0);
+       ipset_type_add(&ipset_hash_netiface1);
        ipset_type_add(&ipset_hash_ipport1);
        ipset_type_add(&ipset_hash_ipportip1);
        ipset_type_add(&ipset_hash_ipportnet1);
        ipset_type_add(&ipset_hash_ipportnet2);
+       ipset_type_add(&ipset_hash_ipportnet3);
        ipset_type_add(&ipset_list_set0);
 }
\ No newline at end of file
index 37b31eb5992c811b830871ec920491cefebabece..3c520481bcb25b59bff3e027f357c1694edb31f5 100644 (file)
@@ -245,6 +245,17 @@ for new entries. If a set is created with timeout support, then the same
 when adding entries. Zero timeout value means the entry is added permanent to the set.
 The timeout value of already added elements can be changed by readding the element
 using the \fB\-exist\fR option.
+
+The \fBhash\fR set types which can store \fBnet\fR type of data (i.e. hash:*net*)
+support the optional
+
+\fBnomatch\fR
+
+option when adding entries. When matching elements in the set, entries marked
+as \fBnomatch\fR are skipped as if those were no added to the set, which makes
+possible to build up sets with exceptions. See the example at hash type
+\fBhash:net\fR below.
+
 .SS bitmap:ip
 The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
 (default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up
@@ -411,7 +422,7 @@ Network address with zero prefix size cannot be stored in this type of sets.
 .PP 
 \fIADD\-ENTRY\fR := \fInetaddr\fR
 .PP 
-\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ] [ \fBnomatch\fR ]
 .PP 
 \fIDEL\-ENTRY\fR := \fInetaddr\fR
 .PP 
@@ -455,7 +466,7 @@ set, or by the host prefix value if the set is empty.
 The lookup time grows linearly with the number of the different prefix
 values added to the set. 
 .PP 
-Examples:
+Example:
 .IP 
 ipset create foo hash:net
 .IP 
@@ -463,7 +474,13 @@ ipset add foo 192.168.0.0/24
 .IP 
 ipset add foo 10.1.0.0/16
 .IP 
-ipset test foo 192.168.0/24
+ipset add foo 192.168.0/24
+.IP 
+ipset add foo 192.168.0/30 nomatch
+.PP 
+When matching the elements in the set above, all IP addresses will match
+from the networks 192.168.0.0/24, 10.1.0.0/16 and 192.168.0/24 except
+192.168.0/30.
 .SS hash:ip,port
 The \fBhash:ip,port\fR set type uses a hash to store IP address and port number pairs.
 The port number is interpreted together with a protocol (default TCP) and zero
@@ -549,7 +566,7 @@ address with zero prefix size is not accepted either.
 .PP 
 \fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
 .PP 
-\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]  [ \fBnomatch\fR ]
 .PP 
 \fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
 .PP 
@@ -659,7 +676,7 @@ address with zero prefix size cannot be stored either.
 .PP 
 \fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
 .PP 
-\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]  [ \fBnomatch\fR ]
 .PP 
 \fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
 .PP 
@@ -720,7 +737,7 @@ accepted.
 .PP 
 \fIADD\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
 .PP 
-\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
+\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]  [ \fBnomatch\fR ]
 .PP 
 \fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fBphysdev\fR:]\fIiface\fR
 .PP 
index 26645ef7037690e9e655d9e08e41ca46facff665..446c512ae29934fd6b75874e4c69a523eead86eb 100644 (file)
 0 n=`ipset list test|grep '^10.0'|wc -l` && test $n -eq 6144
 # Destroy set
 0 ipset -X test
+# Create test set with timeout support
+0 ipset create test hash:ip,port,net timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 2.2.2.2,80,1.1.1.1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 2.2.2.2,80,1.1.1.0/30 
+# Add an overlapping non-matching larger net
+0 ipset -A test 2.2.2.2,80,1.1.1.0/28 nomatch
+# Add an even larger matching net
+0 ipset -A test 2.2.2.2,80,1.1.1.0/26
+# Check non-matching IP
+1 ipset -T test 2.2.2.2,80,1.1.1.1
+# Check matching IP from non-matchin small net
+0 ipset -T test 2.2.2.2,80,1.1.1.3
+# Check non-matching IP from larger net
+1 ipset -T test 2.2.2.2,80,1.1.1.4
+# Check matching IP from even larger net
+0 ipset -T test 2.2.2.2,80,1.1.1.16
+# Update non-matching IP to matching one
+0 ipset -! -A test 2.2.2.2,80,1.1.1.1
+# Delete overlapping small net
+0 ipset -D test 2.2.2.2,80,1.1.1.0/30
+# Check matching IP
+0 ipset -T test 2.2.2.2,80,1.1.1.1
+# Add overlapping small net
+0 ipset -A test 2.2.2.2,80,1.1.1.0/30
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 2.2.2.2,80,1.1.1.1 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 2.2.2.2,80,1.1.1.1
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 2.2.2.2,80,1.1.1.1
+# Check matching IP
+0 ipset -T test 2.2.2.2,80,1.1.1.3
+# Delete test set
+0 ipset destroy test
 # eof
index 71814cf720d01b070e0ffd51bbed9ed006bb028b..1955cadeb49e1f2deee616c721a3c812daa2235a 100644 (file)
 0 n=`ipset list test|grep 1::1|wc -l` && test $n -eq 1026
 # Destroy set
 0 ipset -X test
+# Create test set with timeout support
+0 ipset create test hash:ip,port,net family inet6 timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 2:2:2::2,80,1:1:1::1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 2:2:2::2,80,1:1:1::/124 
+# Add an overlapping non-matching larger net
+0 ipset -A test 2:2:2::2,80,1:1:1::/120 nomatch
+# Add an even larger matching net
+0 ipset -A test 2:2:2::2,80,1:1:1::/116
+# Check non-matching IP
+1 ipset -T test 2:2:2::2,80,1:1:1::1
+# Check matching IP from non-matchin small net
+0 ipset -T test 2:2:2::2,80,1:1:1::F
+# Check non-matching IP from larger net
+1 ipset -T test 2:2:2::2,80,1:1:1::10
+# Check matching IP from even larger net
+0 ipset -T test 2:2:2::2,80,1:1:1::100
+# Update non-matching IP to matching one
+0 ipset -! -A test 2:2:2::2,80,1:1:1::1
+# Delete overlapping small net
+0 ipset -D test 2:2:2::2,80,1:1:1::/124
+# Check matching IP
+0 ipset -T test 2:2:2::2,80,1:1:1::1
+# Add overlapping small net
+0 ipset -A test 2:2:2::2,80,1:1:1::/124
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 2:2:2::2,80,1:1:1::1 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 2:2:2::2,80,1:1:1::1
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 2:2:2::2,80,1:1:1::1
+# Check matching IP
+0 ipset -T test 2:2:2::2,80,1:1:1::F
+# Delete test set
+0 ipset destroy test
 # eof
index ca0ddea0817763ca53b921ba4a79ccaa2ebd6ea9..0ae4d4e2ac2aa05a314436db0588947d03452ce9 100644 (file)
 0 n=`ipset list test | wc -l` && test $n -eq 70
 # Delete test set
 0 ipset destroy test
+# Create test set with timeout support
+0 ipset create test hash:net,iface timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 1.1.1.1,eth0 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1.1.1.0/30,eth0
+# Add an overlapping non-matching larger net
+0 ipset -A test 1.1.1.0/28,eth0 nomatch
+# Add an even larger matching net
+0 ipset -A test 1.1.1.0/26,eth0
+# Check non-matching IP
+1 ipset -T test 1.1.1.1,eth0
+# Check matching IP from non-matchin small net
+0 ipset -T test 1.1.1.3,eth0
+# Check non-matching IP from larger net
+1 ipset -T test 1.1.1.4,eth0
+# Check matching IP from even larger net
+0 ipset -T test 1.1.1.16,eth0
+# Update non-matching IP to matching one
+0 ipset -! -A test 1.1.1.1,eth0
+# Delete overlapping small net
+0 ipset -D test 1.1.1.0/30,eth0
+# Check matching IP
+0 ipset -T test 1.1.1.1,eth0
+# Add overlapping small net
+0 ipset -A test 1.1.1.0/30,eth0
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 1.1.1.1,eth0 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 1.1.1.1,eth0
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 1.1.1.1,eth0
+# Check matching IP
+0 ipset -T test 1.1.1.3,eth0
+# Delete test set
+0 ipset destroy test
 # eof
index 993893d772a1e7aa62f2dfcfc100f6671785c706..5b6047680f4fcdd58da2cbbbd2d03560ecb0b405 100644 (file)
 0 n=`ipset list test|grep '^10.0'|wc -l` && test $n -eq 2052
 # Destroy set
 0 ipset -X test
+# Create test set with timeout support
+0 ipset create test hash:net,port timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 1.1.1.1,80 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1.1.1.0/30,80 
+# Add an overlapping non-matching larger net
+0 ipset -A test 1.1.1.0/28,80 nomatch
+# Add an even larger matching net
+0 ipset -A test 1.1.1.0/26,80
+# Check non-matching IP
+1 ipset -T test 1.1.1.1,80
+# Check matching IP from non-matchin small net
+0 ipset -T test 1.1.1.3,80
+# Check non-matching IP from larger net
+1 ipset -T test 1.1.1.4,80
+# Check matching IP from even larger net
+0 ipset -T test 1.1.1.16,80
+# Update non-matching IP to matching one
+0 ipset -! -A test 1.1.1.1,80
+# Delete overlapping small net
+0 ipset -D test 1.1.1.0/30,80
+# Check matching IP
+0 ipset -T test 1.1.1.1,80
+# Add overlapping small net
+0 ipset -A test 1.1.1.0/30,80
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 1.1.1.1,80 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 1.1.1.1,80
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 1.1.1.1,80
+# Check matching IP
+0 ipset -T test 1.1.1.3,80
+# Delete test set
+0 ipset destroy test
 # eof
index e51186f03daf8edbcd99cb0f86730ebcce2a93cf..10e75d9b77269c653e50274aea822c9ae85b20cc 100644 (file)
 0 ./netgen.sh | ipset restore
 # List set and check the number of elements
 0 n=`ipset -L test|grep '^10.'|wc -l` && test $n -eq 43520
+# Destroy test set
+0 ipset destroy test
+# Create test set with timeout support
+0 ipset create test hash:net timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 1.1.1.1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1.1.1.0/30 
+# Add an overlapping non-matching larger net
+0 ipset -A test 1.1.1.0/28 nomatch
+# Add an even larger matching net
+0 ipset -A test 1.1.1.0/26
+# Check non-matching IP
+1 ipset -T test 1.1.1.1
+# Check matching IP from non-matchin small net
+0 ipset -T test 1.1.1.3
+# Check non-matching IP from larger net
+1 ipset -T test 1.1.1.4
+# Check matching IP from even larger net
+0 ipset -T test 1.1.1.16
+# Update non-matching IP to matching one
+0 ipset -! -A test 1.1.1.1
+# Delete overlapping small net
+0 ipset -D test 1.1.1.0/30
+# Check matching IP
+0 ipset -T test 1.1.1.1
+# Add overlapping small net
+0 ipset -A test 1.1.1.0/30
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 1.1.1.1 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 1.1.1.1
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 1.1.1.1
+# Check matching IP
+0 ipset -T test 1.1.1.3
 # Delete test set
 0 ipset destroy test
 # eof
index 3b58523ecc9fd2e0958943c395b3f6dcb45b9094..063b4c9a9804be695c4dcc8f27d7439a1375d0d6 100644 (file)
 0 n=`ipset list test|grep 1::|wc -l` && test $n -eq 1026
 # Destroy set
 0 ipset -X test
+# Create test set with timeout support
+0 ipset create test hash:net,port family inet6 timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 1:1:1::1,80 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1:1:1::/124,80 
+# Add an overlapping non-matching larger net
+0 ipset -A test 1:1:1::/120,80 nomatch
+# Add an even larger matching net
+0 ipset -A test 1:1:1::/116,80
+# Check non-matching IP
+1 ipset -T test 1:1:1::1,80
+# Check matching IP from non-matchin small net
+0 ipset -T test 1:1:1::F,80
+# Check non-matching IP from larger net
+1 ipset -T test 1:1:1::10,80
+# Check matching IP from even larger net
+0 ipset -T test 1:1:1::100,80
+# Update non-matching IP to matching one
+0 ipset -! -A test 1:1:1::1,80
+# Delete overlapping small net
+0 ipset -D test 1:1:1::/124,80
+# Check matching IP
+0 ipset -T test 1:1:1::1,80
+# Add overlapping small net
+0 ipset -A test 1:1:1::/124,80
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 1:1:1::1,80 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 1:1:1::1,80
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 1:1:1::1,80
+# Check matching IP
+0 ipset -T test 1:1:1::F,80
+# Delete test set
+0 ipset destroy test
 # eof
index 372ed394c9e76f81f468c04d91c073ab559bc46c..22003620e6fa173f60b982f3cb3360b2df7333f8 100644 (file)
 0 ipset flush test
 # Delete test set
 0 ipset destroy test
+# Create test set with timeout support
+0 ipset create test hash:net family inet6 timeout 30
+# Add a non-matching IP address entry
+0 ipset -A test 1:1:1::1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1:1:1::/124 
+# Add an overlapping non-matching larger net
+0 ipset -A test 1:1:1::/120 nomatch
+# Add an even larger matching net
+0 ipset -A test 1:1:1::/116
+# Check non-matching IP
+1 ipset -T test 1:1:1::1
+# Check matching IP from non-matchin small net
+0 ipset -T test 1:1:1::F
+# Check non-matching IP from larger net
+1 ipset -T test 1:1:1::10
+# Check matching IP from even larger net
+0 ipset -T test 1:1:1::100
+# Update non-matching IP to matching one
+0 ipset -! -A test 1:1:1::1
+# Delete overlapping small net
+0 ipset -D test 1:1:1::/124
+# Check matching IP
+0 ipset -T test 1:1:1::1
+# Add overlapping small net
+0 ipset -A test 1:1:1::/124
+# Update matching IP as a non-matching one, with shorter timeout
+0 ipset -! -A test 1:1:1::1 nomatch timeout 2
+# Check non-matching IP
+1 ipset -T test 1:1:1::1
+# Sleep 3s so that element can time out
+0 sleep 3
+# Check non-matching IP
+0 ipset -T test 1:1:1::1
+# Check matching IP
+0 ipset -T test 1:1:1::F
+# Delete test set
+0 ipset destroy test
 # eof
index 22ba63bb9bcfdf7db0a15757bf64af4b86fa6d63..120ff8855617fc24bfe9bf8a60ff8db1c9553b9d 100644 (file)
 0 diff -u -I 'Size in memory.*' .foo ipportnethash.t.list1
 # Network: Flush test set
 0 ipset -F test
-# Network: Delete test set
+# Add a non-matching IP address entry
+0 ipset -A test 2.1.0.0,80,1.1.1.1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 2.1.0.0,80,1.1.1.0/30 
+# Add an overlapping non-matching larger net
+0 ipset -A test 2.1.0.0,80,1.1.1.0/28 nomatch
+# Add an even larger matching net
+0 ipset -A test 2.1.0.0,80,1.1.1.0/26
+# Check non-matching IP
+1 ipset -T test 2.1.0.0,80,1.1.1.1
+# Check matching IP from non-matchin small net
+0 ipset -T test 2.1.0.0,80,1.1.1.3
+# Check non-matching IP from larger net
+1 ipset -T test 2.1.0.0,80,1.1.1.4
+# Check matching IP from even larger net
+0 ipset -T test 2.1.0.0,80,1.1.1.16
+# Update non-matching IP to matching one
+0 ipset -! -A test 2.1.0.0,80,1.1.1.1
+# Delete overlapping small net
+0 ipset -D test 2.1.0.0,80,1.1.1.0/30
+# Check matching IP
+0 ipset -T test 2.1.0.0,80,1.1.1.1
+# Update matching IP as a non-matching one
+0 ipset -! -A test 2.1.0.0,80,1.1.1.1 nomatch
+# Check non-matching IP
+1 ipset -T test 2.1.0.0,80,1.1.1.1
+# Delete test set
 0 ipset -X test
 # eof
index 706aaf4bf74f2c2e14fcaf6611551114db53fef5..40f79fe5fb18b9a39376ec2543c4cb5b2f4704de 100644 (file)
 0 diff -u -I 'Size in memory.*' .foo nethash.t.list0
 # Flush test set
 0 ipset -F test
+# Add a non-matching IP address entry
+0 ipset -A test 1.1.1.1 nomatch
+# Add an overlapping matching small net
+0 ipset -A test 1.1.1.0/30 
+# Add an overlapping non-matching larger net
+0 ipset -A test 1.1.1.0/28 nomatch
+# Add an even larger matching net
+0 ipset -A test 1.1.1.0/26
+# Check non-matching IP
+1 ipset -T test 1.1.1.1
+# Check matching IP from non-matchin small net
+0 ipset -T test 1.1.1.3
+# Check non-matching IP from larger net
+1 ipset -T test 1.1.1.4
+# Check matching IP from even larger net
+0 ipset -T test 1.1.1.16
+# Update non-matching IP to matching one
+0 ipset -! -A test 1.1.1.1
+# Delete overlapping small net
+0 ipset -D test 1.1.1.0/30
+# Check matching IP
+0 ipset -T test 1.1.1.1
+# Update matching IP as a non-matching one
+0 ipset -! -A test 1.1.1.1 nomatch
+# Check non-matching IP
+1 ipset -T test 1.1.1.1
 # Delete test set
 0 ipset -X test
 # eof