diff options
Diffstat (limited to 'net/netfilter/ipset/ip_set_hash_ipportnet.c')
-rw-r--r-- | net/netfilter/ipset/ip_set_hash_ipportnet.c | 137 |
1 files changed, 93 insertions, 44 deletions
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 565a7c5..deb3e3d 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -62,7 +62,8 @@ struct hash_ipportnet4_telem { static inline bool hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, - const struct hash_ipportnet4_elem *ip2) + const struct hash_ipportnet4_elem *ip2, + u32 *multi) { return ip1->ip == ip2->ip && ip1->ip2 == ip2->ip2 && @@ -140,9 +141,19 @@ nla_put_failure: #define HOST_MASK 32 #include <linux/netfilter/ipset/ip_set_ahash.h> +static inline void +hash_ipportnet4_data_next(struct ip_set_hash *h, + const struct hash_ipportnet4_elem *d) +{ + h->next.ip = ntohl(d->ip); + h->next.port = ntohs(d->port); + h->next.ip2 = ntohl(d->ip2); +} + static int hash_ipportnet4_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]; @@ -155,25 +166,26 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - 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); data.ip2 &= ip_set_netmask(data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int hash_ipportnet4_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_ipportnet4_elem data = { .cidr = HOST_MASK }; - u32 ip, ip_to, p, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; + u32 ip2_from = 0, ip2_to, ip2_last, ip2; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -187,21 +199,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); if (ret) return ret; - ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2); + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from); if (ret) return ret; - if (tb[IPSET_ATTR_CIDR2]) + if (tb[IPSET_ATTR_CIDR2]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; - - data.ip2 &= ip_set_netmask(data.cidr); + if (!data.cidr) + return -IPSET_ERR_INVALID_CIDR; + } if (tb[IPSET_ATTR_PORT]) data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); @@ -226,14 +236,16 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; if (adt == IPSET_TEST || - !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || - tb[IPSET_ATTR_PORT_TO])) { - ret = adtfn(set, &data, timeout); + !(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)); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } - ip = ntohl(data.ip); if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -245,29 +257,50 @@ hash_ipportnet4_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); - } else - ip_to = ip; + ip_set_mask_from_to(ip, ip_to, cidr); + } port_to = port = ntohs(data.port); - if (with_ports && tb[IPSET_ATTR_PORT_TO]) { + if (tb[IPSET_ATTR_PORT_TO]) { port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); if (port > port_to) swap(port, port_to); } + if (tb[IPSET_ATTR_IP2_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); + if (ret) + return ret; + if (ip2_from > ip2_to) + swap(ip2_from, ip2_to); + if (ip2_from + UINT_MAX == ip2_to) + return -IPSET_ERR_HASH_RANGE; + } else { + ip_set_mask_from_to(ip2_from, ip2_to, data.cidr); + } - for (; !before(ip_to, ip); ip++) - for (p = port; p <= port_to; p++) { - data.ip = htonl(ip); + if (retried) + ip = h->next.ip; + for (; !before(ip_to, ip); ip++) { + data.ip = htonl(ip); + p = retried && ip == h->next.ip ? h->next.port : port; + for (; p <= port_to; p++) { data.port = htons(p); - ret = adtfn(set, &data, timeout); - - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; + ip2 = retried && ip == h->next.ip && p == h->next.port + ? h->next.ip2 : ip2_from; + while (!after(ip2, ip2_to)) { + data.ip2 = htonl(ip2); + ip2_last = ip_set_range_to_cidr(ip2, ip2_to, + &data.cidr); + ret = adtfn(set, &data, timeout, flags); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip2 = ip2_last + 1; + } } + } return ret; } @@ -303,7 +336,8 @@ struct hash_ipportnet6_telem { static inline bool hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, - const struct hash_ipportnet6_elem *ip2) + const struct hash_ipportnet6_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 && @@ -389,9 +423,17 @@ nla_put_failure: #define HOST_MASK 128 #include <linux/netfilter/ipset/ip_set_ahash.h> +static inline void +hash_ipportnet6_data_next(struct ip_set_hash *h, + const struct hash_ipportnet6_elem *d) +{ + h->next.port = ntohs(d->port); +} + static int hash_ipportnet6_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]; @@ -404,20 +446,20 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, if (adt == IPSET_TEST) data.cidr = HOST_MASK; - 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); ip6_netmask(&data.ip2, data.cidr); - return adtfn(set, &data, h->timeout); + return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } static int hash_ipportnet6_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]; @@ -434,6 +476,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; if (tb[IPSET_ATTR_LINENO]) *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); @@ -478,7 +522,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], } if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &data, timeout); + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -487,9 +531,11 @@ hash_ipportnet6_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; @@ -576,7 +622,9 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = AF_UNSPEC, - .revision = 1, + .revision_min = 0, + /* 1 SCTP and UDPLITE support added */ + .revision_max = 2, /* Range as input support for IPv4 added */ .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -589,6 +637,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_PORT] = { .type = NLA_U16 }, [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, |