netfilter: ipset: fix hash size checking in kernel
[linux-flexiantxendom0-3.2.10.git] / net / netfilter / ipset / ip_set_hash_ipportip.c
index 22e23ab..52f79d8 100644 (file)
@@ -62,7 +62,8 @@ struct hash_ipportip4_telem {
 
 static inline bool
 hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
-                         const struct hash_ipportip4_elem *ip2)
+                         const struct hash_ipportip4_elem *ip2,
+                         u32 *multi)
 {
        return ip1->ip == ip2->ip &&
               ip1->ip2 == ip2->ip2 &&
@@ -127,33 +128,43 @@ nla_put_failure:
 #define HOST_MASK      32
 #include <linux/netfilter/ipset/ip_set_ahash.h>
 
+static inline void
+hash_ipportip4_data_next(struct ip_set_hash *h,
+                        const struct hash_ipportip4_elem *d)
+{
+       h->next.ip = ntohl(d->ip);
+       h->next.port = ntohs(d->port);
+}
+
 static int
 hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
-                   enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+                   const struct xt_action_param *par,
+                   enum ipset_adt adt, const struct ip_set_adt_opt *opt)
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem data = { };
 
-       if (!ip_set_get_ip4_port(skb, flags & IPSET_DIM_TWO_SRC,
+       if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
                return -EINVAL;
 
-       ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
-       ip4addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
+       ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
 
-       return adtfn(set, &data, h->timeout);
+       return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
 
 static int
 hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
-                   enum ipset_adt adt, u32 *lineno, u32 flags)
+                   enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip4_elem data = { };
-       u32 ip, ip_to, p, port, port_to;
+       u32 ip, ip_to = 0, p = 0, port, port_to;
        u32 timeout = h->timeout;
+       bool with_ports = false;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
@@ -180,21 +191,15 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        if (tb[IPSET_ATTR_PROTO]) {
                data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(data.proto);
 
                if (data.proto == 0)
                        return -IPSET_ERR_INVALID_PROTO;
        } else
                return -IPSET_ERR_MISSING_PROTO;
 
-       switch (data.proto) {
-       case IPPROTO_UDP:
-       case IPPROTO_TCP:
-       case IPPROTO_ICMP:
-               break;
-       default:
+       if (!(with_ports || data.proto == IPPROTO_ICMP))
                data.port = 0;
-               break;
-       }
 
        if (tb[IPSET_ATTR_TIMEOUT]) {
                if (!with_timeout(h->timeout))
@@ -203,10 +208,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
        }
 
        if (adt == IPSET_TEST ||
-           !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
            !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
              tb[IPSET_ATTR_PORT_TO])) {
-               ret = adtfn(set, &data, timeout);
+               ret = adtfn(set, &data, timeout, flags);
                return ip_set_eexist(ret, flags) ? 0 : ret;
        }
 
@@ -222,30 +226,32 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 
                if (cidr > 32)
                        return -IPSET_ERR_INVALID_CIDR;
-               ip &= ip_set_hostmask(cidr);
-               ip_to = ip | ~ip_set_hostmask(cidr);
+               ip_set_mask_from_to(ip, ip_to, cidr);
        } else
                ip_to = ip;
 
-       port = ntohs(data.port);
-       if (tb[IPSET_ATTR_PORT_TO]) {
+       port_to = port = ntohs(data.port);
+       if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
                port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
                if (port > port_to)
                        swap(port, port_to);
-       } else
-               port_to = port;
+       }
 
-       for (; !before(ip_to, ip); ip++)
-               for (p = port; p <= port_to; p++) {
+       if (retried)
+               ip = h->next.ip;
+       for (; !before(ip_to, ip); ip++) {
+               p = retried && ip == h->next.ip ? h->next.port : port;
+               for (; p <= port_to; p++) {
                        data.ip = htonl(ip);
                        data.port = htons(p);
-                       ret = adtfn(set, &data, timeout);
+                       ret = adtfn(set, &data, timeout, flags);
 
                        if (ret && !ip_set_eexist(ret, flags))
                                return ret;
                        else
                                ret = 0;
                }
+       }
        return ret;
 }
 
@@ -281,7 +287,8 @@ struct hash_ipportip6_telem {
 
 static inline bool
 hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
-                         const struct hash_ipportip6_elem *ip2)
+                         const struct hash_ipportip6_elem *ip2,
+                         u32 *multi)
 {
        return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
               ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
@@ -348,33 +355,42 @@ nla_put_failure:
 #define HOST_MASK      128
 #include <linux/netfilter/ipset/ip_set_ahash.h>
 
+static inline void
+hash_ipportip6_data_next(struct ip_set_hash *h,
+                        const struct hash_ipportip6_elem *d)
+{
+       h->next.port = ntohs(d->port);
+}
+
 static int
 hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
-                   enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
+                   const struct xt_action_param *par,
+                   enum ipset_adt adt, const struct ip_set_adt_opt *opt)
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem data = { };
 
-       if (!ip_set_get_ip6_port(skb, flags & IPSET_DIM_TWO_SRC,
+       if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
                                 &data.port, &data.proto))
                return -EINVAL;
 
-       ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-       ip6addrptr(skb, flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+       ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
 
-       return adtfn(set, &data, h->timeout);
+       return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
 }
 
 static int
 hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
-                   enum ipset_adt adt, u32 *lineno, u32 flags)
+                   enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
        const struct ip_set_hash *h = set->data;
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_ipportip6_elem data = { };
        u32 port, port_to;
        u32 timeout = h->timeout;
+       bool with_ports = false;
        int ret;
 
        if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
@@ -403,21 +419,15 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 
        if (tb[IPSET_ATTR_PROTO]) {
                data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+               with_ports = ip_set_proto_with_ports(data.proto);
 
                if (data.proto == 0)
                        return -IPSET_ERR_INVALID_PROTO;
        } else
                return -IPSET_ERR_MISSING_PROTO;
 
-       switch (data.proto) {
-       case IPPROTO_UDP:
-       case IPPROTO_TCP:
-       case IPPROTO_ICMPV6:
-               break;
-       default:
+       if (!(with_ports || data.proto == IPPROTO_ICMPV6))
                data.port = 0;
-               break;
-       }
 
        if (tb[IPSET_ATTR_TIMEOUT]) {
                if (!with_timeout(h->timeout))
@@ -425,10 +435,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
                timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
        }
 
-       if (adt == IPSET_TEST ||
-           !(data.proto == IPPROTO_TCP || data.proto == IPPROTO_UDP) ||
-           !tb[IPSET_ATTR_PORT_TO]) {
-               ret = adtfn(set, &data, timeout);
+       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;
        }
 
@@ -437,9 +445,11 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
        if (port > port_to)
                swap(port, port_to);
 
+       if (retried)
+               port = h->next.port;
        for (; port <= port_to; port++) {
                data.port = htons(port);
-               ret = adtfn(set, &data, timeout);
+               ret = adtfn(set, &data, timeout, flags);
 
                if (ret && !ip_set_eexist(ret, flags))
                        return ret;
@@ -457,8 +467,9 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
-       if (!(set->family == AF_INET || set->family == AF_INET6))
+       if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
 
        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
@@ -484,9 +495,12 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
@@ -498,15 +512,15 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        if (tb[IPSET_ATTR_TIMEOUT]) {
                h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 
-               set->variant = set->family == AF_INET
+               set->variant = set->family == NFPROTO_IPV4
                        ? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant;
 
-               if (set->family == AF_INET)
+               if (set->family == NFPROTO_IPV4)
                        hash_ipportip4_gc_init(set);
                else
                        hash_ipportip6_gc_init(set);
        } else {
-               set->variant = set->family == AF_INET
+               set->variant = set->family == NFPROTO_IPV4
                        ? &hash_ipportip4_variant : &hash_ipportip6_variant;
        }
 
@@ -522,8 +536,9 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
        .protocol       = IPSET_PROTOCOL,
        .features       = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2,
        .dimension      = IPSET_DIM_THREE,
-       .family         = AF_UNSPEC,
-       .revision       = 0,
+       .family         = NFPROTO_UNSPEC,
+       .revision_min   = 0,
+       .revision_max   = 1,    /* SCTP and UDPLITE support added */
        .create         = hash_ipportip_create,
        .create_policy  = {
                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },