summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/libipset/data.h1
-rw-r--r--include/libipset/linux_ip_set.h3
-rw-r--r--include/libipset/linux_ip_set_hash.h4
-rw-r--r--include/libipset/parse.h2
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set.h1
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set_ahash.h2
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set_hash.h4
-rw-r--r--kernel/include/linux/netfilter/ipset/pfxlen.h3
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c69
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_net.c51
-rw-r--r--kernel/net/netfilter/ipset/ip_set_hash_netport.c69
-rw-r--r--kernel/net/netfilter/ipset/pfxlen.c21
-rw-r--r--lib/data.c9
-rw-r--r--lib/parse.c53
-rw-r--r--lib/session.c4
-rw-r--r--src/errcode.c4
-rw-r--r--src/ipset.842
-rw-r--r--src/ipset.c22
-rw-r--r--src/ipset_hash_ipport.c6
-rw-r--r--src/ipset_hash_ipportip.c6
-rw-r--r--src/ipset_hash_ipportnet.c101
-rw-r--r--src/ipset_hash_net.c59
-rw-r--r--src/ipset_hash_netport.c82
-rw-r--r--tests/hash:ip,port,net.t8
-rw-r--r--tests/hash:net,port.t8
-rw-r--r--tests/hash:net.t14
-rw-r--r--tests/hash:net.t.list210
27 files changed, 562 insertions, 96 deletions
diff --git a/include/libipset/data.h b/include/libipset/data.h
index 4710963..8902ddf 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -44,6 +44,7 @@ enum ipset_opt {
IPSET_OPT_NAMEREF,
IPSET_OPT_IP2,
IPSET_OPT_CIDR2,
+ IPSET_OPT_IP2_TO,
IPSET_OPT_PROTO,
/* Swap/rename to */
IPSET_OPT_SETNAME2,
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index 00cf0c6..e88f6d5 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -104,6 +104,7 @@ enum {
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
+ IPSET_ATTR_IP2_TO,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
@@ -145,7 +146,7 @@ enum ipset_cmd_flags {
IPSET_FLAG_BIT_LIST_SETNAME = 1,
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_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER),
};
/* Flags at CADT attribute level */
diff --git a/include/libipset/linux_ip_set_hash.h b/include/libipset/linux_ip_set_hash.h
index 7c6336a..7a9e5f7 100644
--- a/include/libipset/linux_ip_set_hash.h
+++ b/include/libipset/linux_ip_set_hash.h
@@ -11,6 +11,10 @@ enum {
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
+ /* Range not supported */
+ IPSET_ERR_HASH_RANGE_UNSUPPORTED,
+ /* Invalid range */
+ IPSET_ERR_HASH_RANGE,
};
#endif /* __IP_SET_HASH_H */
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
index 760df49..bc96a6e 100644
--- a/include/libipset/parse.h
+++ b/include/libipset/parse.h
@@ -60,6 +60,8 @@ extern int ipset_parse_ipnet(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip4_single6(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_ip4_net6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_name(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_before(struct ipset_session *session,
diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h
index ac31e38..fd83f4f 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set.h
@@ -104,6 +104,7 @@ enum {
IPSET_ATTR_NAMEREF,
IPSET_ATTR_IP2,
IPSET_ATTR_CIDR2,
+ IPSET_ATTR_IP2_TO,
__IPSET_ATTR_ADT_MAX,
};
#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1)
diff --git a/kernel/include/linux/netfilter/ipset/ip_set_ahash.h b/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
index 6a4969d..95f6319 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
@@ -353,7 +353,7 @@ retry:
return 0;
}
-static inline void
+static void
type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
/* Add an element to a hash and update the internal counters when succeeded,
diff --git a/kernel/include/linux/netfilter/ipset/ip_set_hash.h b/kernel/include/linux/netfilter/ipset/ip_set_hash.h
index b86f15c..e2a9fae 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set_hash.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set_hash.h
@@ -11,6 +11,10 @@ enum {
IPSET_ERR_INVALID_PROTO,
/* Protocol missing but must be specified */
IPSET_ERR_MISSING_PROTO,
+ /* Range not supported */
+ IPSET_ERR_HASH_RANGE_UNSUPPORTED,
+ /* Invalid range */
+ IPSET_ERR_HASH_RANGE,
};
#ifdef __KERNEL__
diff --git a/kernel/include/linux/netfilter/ipset/pfxlen.h b/kernel/include/linux/netfilter/ipset/pfxlen.h
index 0e1fb50..84efa33 100644
--- a/kernel/include/linux/netfilter/ipset/pfxlen.h
+++ b/kernel/include/linux/netfilter/ipset/pfxlen.h
@@ -3,6 +3,7 @@
#include <asm/byteorder.h>
#include <linux/netfilter.h>
+#include <net/tcp.h>
/* Prefixlen maps, by Jan Engelhardt */
extern const union nf_inet_addr ip_set_netmask_map[];
@@ -32,4 +33,6 @@ ip_set_hostmask6(u8 pfxlen)
return &ip_set_hostmask_map[pfxlen].ip6[0];
}
+extern u32 ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr);
+
#endif /*_PFXLEN_H */
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 0b54fde..ef068b0 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -146,6 +146,7 @@ hash_ipportnet4_data_next(struct ip_set_hash *h,
{
h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
+ h->next.ip2 = ntohl(d->ip2);
}
static int
@@ -181,6 +182,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
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;
@@ -194,21 +196,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]);
@@ -233,14 +233,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])) {
+ !(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)
@@ -254,29 +256,48 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return -IPSET_ERR_INVALID_CIDR;
ip &= ip_set_hostmask(cidr);
ip_to = ip | ~ip_set_hostmask(cidr);
- } else
- ip_to = ip;
+ }
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 {
+ ip2_from &= ip_set_hostmask(data.cidr);
+ ip2_to = ip2_from | ~ip_set_hostmask(data.cidr);
+ }
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.ip = htonl(ip);
data.port = htons(p);
- ret = adtfn(set, &data, timeout, flags);
-
- 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;
@@ -451,6 +472,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]);
@@ -596,7 +619,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
.dimension = IPSET_DIM_THREE,
.family = AF_UNSPEC,
.revision_min = 0,
- .revision_max = 1, /* SCTP and UDPLITE support added */
+ /* 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 },
@@ -609,6 +633,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 },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_net.c b/kernel/net/netfilter/ipset/ip_set_hash_net.c
index 360cf5b..8d3c3ef 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_net.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_net.c
@@ -129,6 +129,7 @@ static inline void
hash_net4_data_next(struct ip_set_hash *h,
const struct hash_net4_elem *d)
{
+ h->next.ip = ntohl(d->ip);
}
static int
@@ -158,6 +159,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
+ u32 ip = 0, ip_to, last;
int ret;
if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -167,27 +169,51 @@ hash_net4_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;
- if (tb[IPSET_ATTR_CIDR])
+ if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
-
- data.ip &= ip_set_netmask(data.cidr);
+ if (!data.cidr)
+ return -IPSET_ERR_INVALID_CIDR;
+ }
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
+
+ if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
+ data.ip = htonl(ip & ip_set_hostmask(data.cidr));
+ ret = adtfn(set, &data, timeout, flags);
+ return ip_set_eexist(ret, flags) ? 0 : ret;
+ }
- ret = adtfn(set, &data, timeout, flags);
-
- return ip_set_eexist(ret, flags) ? 0 : ret;
+ ip_to = ip;
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+ if (ret)
+ return ret;
+ if (ip_to < ip)
+ swap(ip, ip_to);
+ if (ip + UINT_MAX == ip_to)
+ return -IPSET_ERR_HASH_RANGE;
+ }
+ 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);
+ ret = adtfn(set, &data, timeout, flags);
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ ip = last + 1;
+ }
+ return ret;
}
static bool
@@ -334,6 +360,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
if (unlikely(!tb[IPSET_ATTR_IP] ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
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]);
@@ -438,7 +466,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision_min = 0,
- .revision_max = 0,
+ .revision_max = 1, /* Range as input support for IPv4 added */
.create = hash_net_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@@ -449,6 +477,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
},
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netport.c b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
index 09f807f..3001030 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
@@ -141,6 +141,7 @@ static inline void
hash_netport4_data_next(struct ip_set_hash *h,
const struct hash_netport4_elem *d)
{
+ h->next.ip = ntohl(d->ip);
h->next.port = ntohs(d->port);
}
@@ -175,7 +176,7 @@ 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 };
- u32 port, port_to;
+ u32 port, port_to, p = 0, ip = 0, ip_to, last;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
@@ -189,15 +190,15 @@ hash_netport4_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;
- if (tb[IPSET_ATTR_CIDR])
+ if (tb[IPSET_ATTR_CIDR]) {
data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
- if (!data.cidr)
- return -IPSET_ERR_INVALID_CIDR;
- data.ip &= 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]);
@@ -222,26 +223,48 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
+ with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
+ if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
+ data.ip = htonl(ip & ip_set_hostmask(data.cidr));
ret = adtfn(set, &data, timeout, flags);
return ip_set_eexist(ret, flags) ? 0 : ret;
}
- port = ntohs(data.port);
- port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
- if (port > port_to)
- swap(port, port_to);
+ port = port_to = ntohs(data.port);
+ if (tb[IPSET_ATTR_PORT_TO]) {
+ port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
+ if (port_to < port)
+ swap(port, port_to);
+ }
+ if (tb[IPSET_ATTR_IP_TO]) {
+ ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
+ if (ret)
+ return ret;
+ if (ip_to < ip)
+ swap(ip, ip_to);
+ if (ip + UINT_MAX == ip_to)
+ return -IPSET_ERR_HASH_RANGE;
+ } else {
+ ip &= ip_set_hostmask(data.cidr);
+ ip_to = ip | ~ip_set_hostmask(data.cidr);
+ }
if (retried)
- port = h->next.port;
- for (; port <= port_to; port++) {
- data.port = htons(port);
- ret = adtfn(set, &data, timeout, flags);
-
- if (ret && !ip_set_eexist(ret, flags))
- return ret;
- else
- ret = 0;
+ ip = h->next.ip;
+ while (!after(ip, ip_to)) {
+ data.ip = htonl(ip);
+ last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
+ p = retried && ip == h->next.ip ? h->next.port : port;
+ for (; p <= port_to; p++) {
+ data.port = htons(p);
+ ret = adtfn(set, &data, timeout, flags);
+
+ if (ret && !ip_set_eexist(ret, flags))
+ return ret;
+ else
+ ret = 0;
+ }
+ ip = last + 1;
}
return ret;
}
@@ -407,6 +430,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
!ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
!ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
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]);
@@ -545,7 +570,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
.dimension = IPSET_DIM_TWO,
.family = AF_UNSPEC,
.revision_min = 0,
- .revision_max = 1, /* SCTP and UDPLITE support added */
+ /* 1 SCTP and UDPLITE support added */
+ .revision_max = 2, /* Range as input support for IPv4 added */
.create = hash_netport_create,
.create_policy = {
[IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 },
@@ -557,6 +583,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
},
.adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
+ [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_PROTO] = { .type = NLA_U8 },
diff --git a/kernel/net/netfilter/ipset/pfxlen.c b/kernel/net/netfilter/ipset/pfxlen.c
index 23f8c81..b57a856 100644
--- a/kernel/net/netfilter/ipset/pfxlen.c
+++ b/kernel/net/netfilter/ipset/pfxlen.c
@@ -289,3 +289,24 @@ const union nf_inet_addr ip_set_hostmask_map[] = {
E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF),
};
EXPORT_SYMBOL_GPL(ip_set_hostmask_map);
+
+/* Find the largest network which matches the range from left, in host order. */
+u32
+ip_set_range_to_cidr(u32 from, u32 to, u8 *cidr)
+{
+ u32 last;
+ u8 i;
+
+ for (i = 1; i < 32; i++) {
+ if ((from & ip_set_hostmask(i)) != from)
+ continue;
+ last = from | ~ip_set_hostmask(i);
+ if (!after(last, to)) {
+ *cidr = i;
+ return last;
+ }
+ }
+ *cidr = 32;
+ return from;
+}
+EXPORT_SYMBOL_GPL(ip_set_range_to_cidr);
diff --git a/lib/data.c b/lib/data.c
index 022e4b4..1541728 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -66,6 +66,7 @@ struct ipset_data {
/* ADT/LIST/SAVE */
struct {
union nf_inet_addr ip2;
+ union nf_inet_addr ip2_to;
uint8_t cidr2;
uint8_t proto;
char ether[ETH_ALEN];
@@ -289,6 +290,11 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
return -1;
copy_addr(data->family, &data->adt.ip2, value);
break;
+ case IPSET_OPT_IP2_TO:
+ if (!(data->family == AF_INET || data->family == AF_INET6))
+ return -1;
+ copy_addr(data->family, &data->adt.ip2_to, value);
+ break;
case IPSET_OPT_CIDR2:
data->adt.cidr2 = *(const uint8_t *) value;
break;
@@ -401,6 +407,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return data->adt.nameref;
case IPSET_OPT_IP2:
return &data->adt.ip2;
+ case IPSET_OPT_IP2_TO:
+ return &data->adt.ip2_to;
case IPSET_OPT_CIDR2:
return &data->adt.cidr2;
case IPSET_OPT_PROTO:
@@ -436,6 +444,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
case IPSET_OPT_IP:
case IPSET_OPT_IP_TO:
case IPSET_OPT_IP2:
+ case IPSET_OPT_IP2_TO:
return family == AF_INET ? sizeof(uint32_t)
: sizeof(struct in6_addr);
case IPSET_OPT_PORT:
diff --git a/lib/parse.c b/lib/parse.c
index 0c15231..091fc6d 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -667,8 +667,15 @@ parse_ipaddr(struct ipset_session *session,
char *saved = strdup(str);
char *a, *tmp = saved;
struct addrinfo *info;
- enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR
- : IPSET_OPT_CIDR2;
+ enum ipset_opt copt, opt2;
+
+ if (opt == IPSET_OPT_IP) {
+ copt = IPSET_OPT_CIDR;
+ opt2 = IPSET_OPT_IP_TO;
+ } else {
+ copt = IPSET_OPT_CIDR2;
+ opt2 = IPSET_OPT_IP2_TO;
+ }
if (tmp == NULL)
return ipset_err(session,
@@ -691,7 +698,7 @@ parse_ipaddr(struct ipset_session *session,
|| !range)
goto out;
freeaddrinfo(info);
- aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family);
+ aerr = get_addrinfo(session, opt2, a, &info, family);
out:
if (aerr != EINVAL)
@@ -974,6 +981,46 @@ ipset_parse_ip4_single6(struct ipset_session *session,
}
/**
+ * ipset_parse_ip4_net6 - parse IPv4|IPv6 address or address/cidr pattern
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an IPv4|IPv6 address or address/cidr pattern. For IPv4,
+ * address range is valid too.
+ * If family is not set yet in the data blob, INET is assumed.
+ * The values are stored in the data blob of the session.
+ *
+ * FIXME: if the hostname resolves to multiple addresses,
+ * the first one is used only.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_ip4_net6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ struct ipset_data *data;
+ uint8_t family;
+
+ assert(session);
+ assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+ assert(str);
+
+ data = ipset_session_data(session);
+ family = ipset_data_family(data);
+
+ if (family == AF_UNSPEC) {
+ family = AF_INET;
+ ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+ }
+
+ return family == AF_INET ? parse_ip(session, opt, str, IPADDR_ANY)
+ : ipset_parse_ipnet(session, opt, str);
+
+}
+
+/**
* ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout
* @session: session structure
* @opt: option kind of the data
diff --git a/lib/session.c b/lib/session.c
index 59405d4..a9c7c33 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -469,6 +469,10 @@ static const struct ipset_attr_policy adt_attrs[] = {
.type = MNL_TYPE_U8,
.opt = IPSET_OPT_CIDR2,
},
+ [IPSET_ATTR_IP2_TO] = {
+ .type = MNL_TYPE_NESTED,
+ .opt = IPSET_OPT_IP2_TO,
+ },
};
static const struct ipset_attr_policy ipaddr_attrs[] = {
diff --git a/src/errcode.c b/src/errcode.c
index 6370ec7..9d5f226 100644
--- a/src/errcode.c
+++ b/src/errcode.c
@@ -113,6 +113,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = {
"Invalid protocol specified" },
{ IPSET_ERR_MISSING_PROTO, 0,
"Protocol missing, but must be specified" },
+ { IPSET_ERR_HASH_RANGE_UNSUPPORTED, 0,
+ "Range is not supported in the \"net\" component of the element" },
+ { IPSET_ERR_HASH_RANGE, 0,
+ "Invalid range, covers the whole address space" },
{ },
};
diff --git a/src/ipset.8 b/src/ipset.8
index 21750fa..90914f4 100644
--- a/src/ipset.8
+++ b/src/ipset.8
@@ -214,7 +214,7 @@ command follows the syntax
where the current list of the methods are
\fBbitmap\fR, \fBhash\fR, and \fBlist\fR and the possible data types
-are \fBip\fR, \fBmac\fR and \fBport\fR. The dimension of a set
+are \fBip\fR, \fBnet\fR, \fBmac\fR and \fBport\fR. The dimension of a set
is equal to the number of data types in its type name.
When adding, deleting or testing entries in a set, the same comma separated
@@ -409,13 +409,16 @@ Network address with zero prefix size cannot be stored in this type of sets.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fIADD\-ENTRY\fR := \fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fIDEL\-ENTRY\fR := \fInetaddr\fR
.PP
-\fITEST\-ENTRY\fR := \fIip\fR[/\fIcidr\fR]
+\fITEST\-ENTRY\fR := \fInetaddr\fR
+.PP
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -431,6 +434,11 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
+For the \fBinet\fR family one can add or delete multiple entries by specifying
+a range, which is converted internally to network(s) equal to the range:
+.PP
+\fInetaddr\fR := { \fIip\fR[/\fIcidr\fR] | \fIfromaddr\fR\-\fItoaddr\fR }
+.PP
When adding/deleting/testing entries, if the cidr prefix parameter is not specified,
then the host prefix value is assumed. When adding/deleting entries, the exact
element is added/deleted and overlapping elements are not checked by the kernel.
@@ -539,13 +547,16 @@ address with zero prefix size is not accepted either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+\fIADD\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+\fIDEL\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
-\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR],[\fIproto\fR:]\fIport\fR
+\fITEST\-ENTRY\fR := \fInetaddr\fR,[\fIproto\fR:]\fIport\fR
+.PP
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -561,7 +572,8 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
-For the
+For the \fInetaddr\fR part of the elements
+see the description at the \fBhash:net\fR set type. For the
[\fIproto\fR:]\fIport\fR
part of the elements see the description at the
\fBhash:ip,port\fR set type.
@@ -645,18 +657,22 @@ address with zero prefix size cannot be stored either.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR | \fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
-\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIip\fR[/\fIcidr\fR]
+\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fInetaddr\fR
.PP
-For the first \fIipaddr\fR and
+where
+\fInetaddr\fR := \fIip\fR[/\fIcidr\fR]
+.PP
+For the \fIipaddr\fR and
[\fIproto\fR:]\fIport\fR
parts of the elements see the descriptions at the
-\fBhash:ip,port\fR set type.
+\fBhash:ip,port\fR set type. For the \fInetaddr\fR part of the elements
+see the description at the \fBhash:net\fR set type.
.PP
Optional \fBcreate\fR options:
.TP
diff --git a/src/ipset.c b/src/ipset.c
index 05f8ef3..b3569ed 100644
--- a/src/ipset.c
+++ b/src/ipset.c
@@ -39,10 +39,13 @@ extern struct ipset_type ipset_bitmap_ipmac0;
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_netport0;
-extern struct ipset_type ipset_hash_ipport0;
-extern struct ipset_type ipset_hash_ipportip0;
-extern struct ipset_type ipset_hash_ipportnet0;
+extern struct ipset_type ipset_hash_net1;
+extern struct ipset_type ipset_hash_netport1;
+extern struct ipset_type ipset_hash_netport2;
+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_list_set0;
enum exittype {
@@ -721,10 +724,13 @@ main(int argc, char *argv[])
ipset_type_add(&ipset_bitmap_port0);
ipset_type_add(&ipset_hash_ip0);
ipset_type_add(&ipset_hash_net0);
- ipset_type_add(&ipset_hash_netport0);
- ipset_type_add(&ipset_hash_ipport0);
- ipset_type_add(&ipset_hash_ipportip0);
- ipset_type_add(&ipset_hash_ipportnet0);
+ ipset_type_add(&ipset_hash_net1);
+ ipset_type_add(&ipset_hash_netport1);
+ ipset_type_add(&ipset_hash_netport2);
+ 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_list_set0);
/* Initialize session */
diff --git a/src/ipset_hash_ipport.c b/src/ipset_hash_ipport.c
index 3179805..58ea76c 100644
--- a/src/ipset_hash_ipport.c
+++ b/src/ipset_hash_ipport.c
@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipport_add_args[] = {
{ },
};
-static const char hash_ipport_usage[] =
+static const char hash_ipport1_usage[] =
"create SETNAME hash:ip,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -85,7 +85,7 @@ static const char hash_ipport_usage[] =
" 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_ipport0 = {
+struct ipset_type ipset_hash_ipport1 = {
.name = "hash:ip,port",
.alias = { "ipporthash", NULL },
.revision = 1,
@@ -139,6 +139,6 @@ struct ipset_type ipset_hash_ipport0 = {
| IPSET_FLAG(IPSET_OPT_PROTO),
},
- .usage = hash_ipport_usage,
+ .usage = hash_ipport1_usage,
.usagefn = ipset_port_usage,
};
diff --git a/src/ipset_hash_ipportip.c b/src/ipset_hash_ipportip.c
index 944ee81..2f310ea 100644
--- a/src/ipset_hash_ipportip.c
+++ b/src/ipset_hash_ipportip.c
@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportip_add_args[] = {
{ },
};
-static const char hash_ipportip_usage[] =
+static const char hash_ipportip1_usage[] =
"create SETNAME hash:ip,port,ip\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -85,7 +85,7 @@ static const char hash_ipportip_usage[] =
" 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_ipportip0 = {
+struct ipset_type ipset_hash_ipportip1 = {
.name = "hash:ip,port,ip",
.alias = { "ipportiphash", NULL },
.revision = 1,
@@ -150,6 +150,6 @@ struct ipset_type ipset_hash_ipportip0 = {
| IPSET_FLAG(IPSET_OPT_IP2),
},
- .usage = hash_ipportip_usage,
+ .usage = hash_ipportip1_usage,
.usagefn = ipset_port_usage,
};
diff --git a/src/ipset_hash_ipportnet.c b/src/ipset_hash_ipportnet.c
index bd94d12..c2eece8 100644
--- a/src/ipset_hash_ipportnet.c
+++ b/src/ipset_hash_ipportnet.c
@@ -70,7 +70,7 @@ static const struct ipset_arg hash_ipportnet_add_args[] = {
{ },
};
-static const char hash_ipportnet_usage[] =
+static const char hash_ipportnet1_usage[] =
"create SETNAME hash:ip,port,net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -86,7 +86,7 @@ static const char hash_ipportnet_usage[] =
" 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_ipportnet0 = {
+struct ipset_type ipset_hash_ipportnet1 = {
.name = "hash:ip,port,net",
.alias = { "ipportnethash", NULL },
.revision = 1,
@@ -133,6 +133,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
| 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)
@@ -141,6 +142,7 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_CIDR2)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[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)
@@ -154,6 +156,99 @@ struct ipset_type ipset_hash_ipportnet0 = {
| IPSET_FLAG(IPSET_OPT_CIDR2),
},
- .usage = hash_ipportnet_usage,
+ .usage = hash_ipportnet1_usage,
.usagefn = ipset_port_usage,
};
+
+static const char hash_ipportnet2_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]\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_ipportnet2 = {
+ .name = "hash:ip,port,net",
+ .alias = { "ipportnethash", NULL },
+ .revision = 2,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_THREE,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_single6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO] = {
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
+ .opt = IPSET_OPT_PORT
+ },
+ [IPSET_DIM_THREE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP2
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_ipportnet_create_args,
+ [IPSET_ADD] = hash_ipportnet_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_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_ipportnet2_usage,
+ .usagefn = ipset_port_usage,
+};
+
diff --git a/src/ipset_hash_net.c b/src/ipset_hash_net.c
index e8891c1..9c0a6ca 100644
--- a/src/ipset_hash_net.c
+++ b/src/ipset_hash_net.c
@@ -57,7 +57,7 @@ static const struct ipset_arg hash_net_add_args[] = {
{ },
};
-static const char hash_net_usage[] =
+static const char hash_net0_usage[] =
"create SETNAME hash:net\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -105,5 +105,60 @@ struct ipset_type ipset_hash_net0 = {
| IPSET_FLAG(IPSET_OPT_CIDR),
},
- .usage = hash_net_usage,
+ .usage = hash_net0_usage,
};
+
+static const char hash_net1_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]\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_net1 = {
+ .name = "hash:net",
+ .alias = { "nethash", NULL },
+ .revision = 1,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_ONE,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_net_create_args,
+ [IPSET_ADD] = hash_net_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_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_net1_usage,
+};
+
diff --git a/src/ipset_hash_netport.c b/src/ipset_hash_netport.c
index 8ca77df..7964319 100644
--- a/src/ipset_hash_netport.c
+++ b/src/ipset_hash_netport.c
@@ -49,7 +49,7 @@ static const struct ipset_arg hash_netport_add_args[] = {
{ },
};
-static const char hash_netport_usage[] =
+static const char hash_netport1_usage[] =
"create SETNAME hash:net,port\n"
" [family inet|inet6]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
@@ -63,7 +63,7 @@ static const char hash_netport_usage[] =
" 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_netport0 = {
+struct ipset_type ipset_hash_netport1 = {
.name = "hash:net,port",
.alias = { "netporthash", NULL },
.revision = 1,
@@ -118,6 +118,82 @@ struct ipset_type ipset_hash_netport0 = {
| IPSET_FLAG(IPSET_OPT_CIDR),
},
- .usage = hash_netport_usage,
+ .usage = hash_netport1_usage,
+ .usagefn = ipset_port_usage,
+};
+
+static const char hash_netport2_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]\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_netport2 = {
+ .name = "hash:net,port",
+ .alias = { "netporthash", NULL },
+ .revision = 2,
+ .family = AF_INET46,
+ .dimension = IPSET_DIM_TWO,
+ .elem = {
+ [IPSET_DIM_ONE] = {
+ .parse = ipset_parse_ip4_net6,
+ .print = ipset_print_ip,
+ .opt = IPSET_OPT_IP
+ },
+ [IPSET_DIM_TWO] = {
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
+ .opt = IPSET_OPT_PORT
+ },
+ },
+ .args = {
+ [IPSET_CREATE] = hash_netport_create_args,
+ [IPSET_ADD] = hash_netport_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_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_netport2_usage,
.usagefn = ipset_port_usage,
};
diff --git a/tests/hash:ip,port,net.t b/tests/hash:ip,port,net.t
index 89b046c..c13ccc4 100644
--- a/tests/hash:ip,port,net.t
+++ b/tests/hash:ip,port,net.t
@@ -46,4 +46,12 @@
0 n=`ipset list test|grep 10.0|wc -l` && test $n -eq 3072
# Destroy set
0 ipset -X test
+# Create set to add a range and with range notation in the network
+0 ipset new test hash:ip,port,net hashsize 64
+# Add a range which forces a resizing
+0 ipset add test 10.0.0.0-10.0.3.255,tcp:80-82,192.168.0.0-192.168.2.255
+# Check that correct number of elements are added
+0 n=`ipset list test|grep 10.0|wc -l` && test $n -eq 6144
+# Destroy set
+0 ipset -X test
# eof
diff --git a/tests/hash:net,port.t b/tests/hash:net,port.t
index d5f420c..519292c 100644
--- a/tests/hash:net,port.t
+++ b/tests/hash:net,port.t
@@ -62,4 +62,12 @@
0 n=`ipset list test|grep 10.0|wc -l` && test $n -eq 1026
# Destroy set
0 ipset -X test
+# Create set to add a range and with range notation in the network
+0 ipset new test hash:net,port hashsize 64
+# Add a range which forces a resizing
+0 ipset add test 10.0.0.0-10.0.2.255,tcp:80-1105
+# Check that correct number of elements are added
+0 n=`ipset list test|grep 10.0|wc -l` && test $n -eq 2052
+# Destroy set
+0 ipset -X test
# eof
diff --git a/tests/hash:net.t b/tests/hash:net.t
index 9947bf6..8ccc3f2 100644
--- a/tests/hash:net.t
+++ b/tests/hash:net.t
@@ -38,12 +38,22 @@
0 diff -u -I 'Size in memory.*' .foo hash:net.t.list0
# Sleep 5s so that element can time out
0 sleep 5
-# IP: List set
+# List set
0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0
-# IP: Check listing
+# Check listing
0 diff -u -I 'Size in memory.*' .foo hash:net.t.list1
# Flush test set
0 ipset flush test
# Delete test set
0 ipset destroy test
+# Create test set
+0 ipset new test hash:net
+# Add networks in range notation
+0 ipset add test 10.2.0.0-10.2.1.12
+# List set
+0 ipset -L test 2>/dev/null > .foo0 && ./sort.sh .foo0
+# Check listing
+0 diff -u -I 'Size in memory.*' .foo hash:net.t.list2
+# Delete test set
+0 ipset destroy test
# eof
diff --git a/tests/hash:net.t.list2 b/tests/hash:net.t.list2
new file mode 100644
index 0000000..dad3f22
--- /dev/null
+++ b/tests/hash:net.t.list2
@@ -0,0 +1,10 @@
+Name: test
+Type: hash:net
+Header: family inet hashsize 1024 maxelem 65536
+Size in memory: 17016
+References: 0
+Members:
+10.2.0.0/24
+10.2.1.0/29
+10.2.1.12
+10.2.1.8/30