*skbinfo = ext->skbinfo;
 }
 
+static inline void
+nf_inet_addr_mask_inplace(union nf_inet_addr *a1,
+                         const union nf_inet_addr *mask)
+{
+       a1->all[0] &= mask->all[0];
+       a1->all[1] &= mask->all[1];
+       a1->all[2] &= mask->all[2];
+       a1->all[3] &= mask->all[3];
+}
+
 #define IP_SET_INIT_KEXT(skb, opt, set)                        \
        { .bytes = (skb)->len, .packets = 1, .target = true,\
          .timeout = ip_set_adt_opt_timeout(opt, set) }
 
        IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,     /* 9 */
        IPSET_ATTR_MARK,        /* 10 */
        IPSET_ATTR_MARKMASK,    /* 11 */
+       IPSET_ATTR_BITMASK,     /* 12 */
        /* Reserve empty slots */
        IPSET_ATTR_CADT_MAX = 16,
        /* Create-only specific attributes */
        IPSET_ERR_COMMENT,
        IPSET_ERR_INVALID_MARKMASK,
        IPSET_ERR_SKBINFO,
+       IPSET_ERR_BITMASK_NETMASK_EXCL,
 
        /* Type specific error codes */
        IPSET_ERR_TYPE_SPECIFIC = 4352,
 
        (SET_WITH_TIMEOUT(set) &&       \
         ip_set_timeout_expired(ext_timeout(d, set)))
 
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+static const union nf_inet_addr onesmask = {
+       .all[0] = 0xffffffff,
+       .all[1] = 0xffffffff,
+       .all[2] = 0xffffffff,
+       .all[3] = 0xffffffff
+};
+
+static const union nf_inet_addr zeromask = {};
+#endif
+
 #endif /* _IP_SET_HASH_GEN_H */
 
 #ifndef MTYPE
        u32 markmask;           /* markmask value for mark mask to store */
 #endif
        u8 bucketsize;          /* max elements in an array block */
-#ifdef IP_SET_HASH_WITH_NETMASK
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
        u8 netmask;             /* netmask value for subnets to store */
+       union nf_inet_addr bitmask;     /* stores bitmask */
 #endif
        struct list_head ad;    /* Resize add|del backlist */
        struct mtype_elem next; /* temporary storage for uadd */
        /* Resizing changes htable_bits, so we ignore it */
        return x->maxelem == y->maxelem &&
               a->timeout == b->timeout &&
-#ifdef IP_SET_HASH_WITH_NETMASK
-              x->netmask == y->netmask &&
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+              nf_inet_addr_cmp(&x->bitmask, &y->bitmask) &&
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
               x->markmask == y->markmask &&
                          htonl(jhash_size(htable_bits))) ||
            nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
                goto nla_put_failure;
+#ifdef IP_SET_HASH_WITH_BITMASK
+       /* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask
+        * and not bitmask. These two are mutually exclusive. */
+       if (h->netmask == HOST_MASK && !nf_inet_addr_cmp(&onesmask, &h->bitmask)) {
+               if (set->family == NFPROTO_IPV4) {
+                       if (nla_put_ipaddr4(skb, IPSET_ATTR_BITMASK, h->bitmask.ip))
+                               goto nla_put_failure;
+               } else if (set->family == NFPROTO_IPV6) {
+                       if (nla_put_ipaddr6(skb, IPSET_ATTR_BITMASK, &h->bitmask.in6))
+                               goto nla_put_failure;
+               }
+       }
+#endif
 #ifdef IP_SET_HASH_WITH_NETMASK
-       if (h->netmask != HOST_MASK &&
-           nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
+       if (h->netmask != HOST_MASK && nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
                goto nla_put_failure;
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
        u32 markmask;
 #endif
        u8 hbits;
-#ifdef IP_SET_HASH_WITH_NETMASK
-       u8 netmask;
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+       int ret __attribute__((unused)) = 0;
+       u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
+       union nf_inet_addr bitmask = onesmask;
 #endif
        size_t hsize;
        struct htype *h;
 #endif
 
 #ifdef IP_SET_HASH_WITH_NETMASK
-       netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
        if (tb[IPSET_ATTR_NETMASK]) {
                netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
 
                    (set->family == NFPROTO_IPV6 && netmask > 128) ||
                    netmask == 0)
                        return -IPSET_ERR_INVALID_NETMASK;
+
+               /* we convert netmask to bitmask and store it */
+               if (set->family == NFPROTO_IPV4)
+                       bitmask.ip = ip_set_netmask(netmask);
+               else
+                       ip6_netmask(&bitmask, netmask);
+       }
+#endif
+
+#ifdef IP_SET_HASH_WITH_BITMASK
+       if (tb[IPSET_ATTR_BITMASK]) {
+               /* bitmask and netmask do the same thing, allow only one of these options */
+               if (tb[IPSET_ATTR_NETMASK])
+                       return -IPSET_ERR_BITMASK_NETMASK_EXCL;
+
+               if (set->family == NFPROTO_IPV4) {
+                       ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_BITMASK], &bitmask.ip);
+                       if (ret || !bitmask.ip)
+                               return -IPSET_ERR_INVALID_NETMASK;
+               } else if (set->family == NFPROTO_IPV6) {
+                       ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_BITMASK], &bitmask);
+                       if (ret || ipv6_addr_any(&bitmask.in6))
+                               return -IPSET_ERR_INVALID_NETMASK;
+               }
+
+               if (nf_inet_addr_cmp(&bitmask, &zeromask))
+                       return -IPSET_ERR_INVALID_NETMASK;
        }
 #endif
 
        for (i = 0; i < ahash_numof_locks(hbits); i++)
                spin_lock_init(&t->hregion[i].lock);
        h->maxelem = maxelem;
-#ifdef IP_SET_HASH_WITH_NETMASK
+#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
+       h->bitmask = bitmask;
        h->netmask = netmask;
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
 
 /*                             2          Comments support */
 /*                             3          Forceadd support */
 /*                             4          skbinfo support */
-#define IPSET_TYPE_REV_MAX     5       /* bucketsize, initval support  */
+/*                             5          bucketsize, initval support  */
+#define IPSET_TYPE_REV_MAX     6       /* bitmask support  */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
 /* Type specific function prefix */
 #define HTYPE          hash_ip
 #define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
 
 /* IPv4 variant */
 
        __be32 ip;
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
-       ip &= ip_set_netmask(h->netmask);
+       ip &= h->bitmask.ip;
        if (ip == 0)
                return -EINVAL;
 
        if (ret)
                return ret;
 
-       ip &= ip_set_hostmask(h->netmask);
+       ip &= ntohl(h->bitmask.ip);
        e.ip = htonl(ip);
        if (e.ip == 0)
                return -IPSET_ERR_HASH_ELEM;
        return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
 }
 
-static void
-hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-       ip6_netmask(ip, prefix);
-}
-
 static bool
 hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
 {
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
-       hash_ip6_netmask(&e.ip, h->netmask);
+       nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
        if (ipv6_addr_any(&e.ip.in6))
                return -EINVAL;
 
        if (ret)
                return ret;
 
-       hash_ip6_netmask(&e.ip, h->netmask);
+       nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
        if (ipv6_addr_any(&e.ip.in6))
                return -IPSET_ERR_HASH_ELEM;
 
                [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
                [IPSET_ATTR_NETMASK]    = { .type = NLA_U8  },
+               [IPSET_ATTR_BITMASK]    = { .type = NLA_NESTED },
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
        },
        .adt_policy     = {
 
 /*                             3    Comments support added */
 /*                             4    Forceadd support added */
 /*                             5    skbinfo support added */
-#define IPSET_TYPE_REV_MAX     6 /* bucketsize, initval support added */
+/*                             6    bucketsize, initval support added */
+#define IPSET_TYPE_REV_MAX     7 /* bitmask support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
 
 /* Type specific function prefix */
 #define HTYPE          hash_ipport
+#define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
 
 /* IPv4 variant */
 
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport4_elem e = { .ip = 0 };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+       const struct MTYPE *h = set->data;
 
        if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
                return -EINVAL;
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+       e.ip &= h->bitmask.ip;
+       if (e.ip == 0)
+               return -EINVAL;
        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
        if (ret)
                return ret;
 
+       e.ip &= h->bitmask.ip;
+       if (e.ip == 0)
+               return -EINVAL;
+
        e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 
        if (tb[IPSET_ATTR_PROTO]) {
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipport6_elem e = { .ip = { .all = { 0 } } };
        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
+       const struct MTYPE *h = set->data;
 
        if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &e.port, &e.proto))
                return -EINVAL;
 
        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+       nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
+       if (ipv6_addr_any(&e.ip.in6))
+               return -EINVAL;
+
        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
        if (ret)
                return ret;
 
+       nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
+       if (ipv6_addr_any(&e.ip.in6))
+               return -EINVAL;
+
        e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 
        if (tb[IPSET_ATTR_PROTO]) {
                [IPSET_ATTR_PROTO]      = { .type = NLA_U8 },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_NETMASK]    = { .type = NLA_U8 },
+               [IPSET_ATTR_BITMASK]    = { .type = NLA_NESTED },
        },
        .adt_policy     = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
 
 #define IPSET_TYPE_REV_MIN     0
 /*                             1          Forceadd support added */
 /*                             2          skbinfo support added */
-#define IPSET_TYPE_REV_MAX     3       /* bucketsize, initval support added */
+/*                             3          bucketsize, initval support added */
+#define IPSET_TYPE_REV_MAX     4       /* bitmask support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
 /* Type specific function prefix */
 #define HTYPE          hash_netnet
 #define IP_SET_HASH_WITH_NETS
+#define IP_SET_HASH_WITH_NETMASK
+#define IP_SET_HASH_WITH_BITMASK
 #define IPSET_NET_COUNT 2
 
 /* IPv4 variants */
 
        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
        ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
-       e.ip[0] &= ip_set_netmask(e.cidr[0]);
-       e.ip[1] &= ip_set_netmask(e.cidr[1]);
+       e.ip[0] &= (ip_set_netmask(e.cidr[0]) & h->bitmask.ip);
+       e.ip[1] &= (ip_set_netmask(e.cidr[1]) & h->bitmask.ip);
 
        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
        if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] ||
                                   tb[IPSET_ATTR_IP2_TO])) {
-               e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0]));
-               e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1]));
+               e.ip[0] = htonl(ip & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[0]));
+               e.ip[1] = htonl(ip2_from & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[1]));
                ret = adtfn(set, &e, &ext, &ext, flags);
                return ip_set_enomatch(ret, flags, adt, set) ? -ret :
                       ip_set_eexist(ret, flags) ? 0 : ret;
        ip6_netmask(&e.ip[0], e.cidr[0]);
        ip6_netmask(&e.ip[1], e.cidr[1]);
 
+       nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
+       nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
+       if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
+               return -EINVAL;
+
        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netnet6_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
+       const struct hash_netnet6 *h = set->data;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
        ip6_netmask(&e.ip[0], e.cidr[0]);
        ip6_netmask(&e.ip[1], e.cidr[1]);
 
+       nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
+       nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
+       if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
+               return -IPSET_ERR_HASH_ELEM;
+
        if (tb[IPSET_ATTR_CADT_FLAGS]) {
                u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 
                [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
+               [IPSET_ATTR_NETMASK]    = { .type = NLA_U8 },
+               [IPSET_ATTR_BITMASK]    = { .type = NLA_NESTED },
        },
        .adt_policy     = {
                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },