netfilter: ipset: Add support for new bitmask parameter
[sfrench/cifs-2.6.git] / net / netfilter / ipset / ip_set_hash_netnet.c
index 3d09eefe998a7f82f9549d141daeb13e311f3486..cdfb78c6e0d3de56488cebc5a34df470a7e5ed36 100644 (file)
@@ -23,7 +23,8 @@
 #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>");
@@ -33,6 +34,8 @@ MODULE_ALIAS("ip_set_hash:net,net");
 /* 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 */
@@ -153,8 +156,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
 
        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);
 }
@@ -213,8 +216,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        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;
@@ -404,6 +407,11 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
        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);
 }
 
@@ -414,6 +422,7 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
        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])
@@ -453,6 +462,11 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
        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]);
 
@@ -484,6 +498,8 @@ static struct ip_set_type hash_netnet_type __read_mostly = {
                [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 },