summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-22 10:49:41 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-06-22 10:49:41 +0200
commit020936c8c3375e1efe44a3087c891a4b2cbfe044 (patch)
treea94751e6f1f11bcf118129c343d1942bbf53e808
parent97a12ba3f184a76c406eb5622ec21a4d4d6fc8bf (diff)
ipset 5: last new feature addedv5.0-pre3
- the hash types can now store protocol together port, not only port - lots of fixes everywhere: parser, error reporting, manpage The last bits on the todo list before announcing ipset 5: - recheck all the error messages - add possibly more tests - polish manpage
-rw-r--r--Make_global.am5
-rw-r--r--include/libipset/data.h2
-rw-r--r--include/libipset/linux_ip_set.h74
-rw-r--r--include/libipset/linux_ip_set_hash.h2
-rw-r--r--include/libipset/parse.h5
-rw-r--r--include/libipset/print.h6
-rw-r--r--include/libipset/session.h1
-rw-r--r--kernel/include/linux/netfilter/ip_set.h80
-rw-r--r--kernel/include/linux/netfilter/ip_set_chash.h12
-rw-r--r--kernel/include/linux/netfilter/ip_set_getport.h99
-rw-r--r--kernel/include/linux/netfilter/ip_set_hash.h2
-rw-r--r--kernel/ip_set.c1
-rw-r--r--kernel/ip_set_bitmap_ip.c23
-rw-r--r--kernel/ip_set_bitmap_ipmac.c11
-rw-r--r--kernel/ip_set_bitmap_port.c31
-rw-r--r--kernel/ip_set_hash_ip.c25
-rw-r--r--kernel/ip_set_hash_ipport.c131
-rw-r--r--kernel/ip_set_hash_ipportip.c128
-rw-r--r--kernel/ip_set_hash_ipportnet.c128
-rw-r--r--kernel/ip_set_hash_net.c43
-rw-r--r--kernel/ip_set_list_set.c16
-rw-r--r--lib/data.c7
-rw-r--r--lib/parse.c83
-rw-r--r--lib/print.c73
-rw-r--r--lib/session.c25
-rw-r--r--src/errcode.c6
-rw-r--r--src/ipset.896
-rw-r--r--src/ipset.c31
-rw-r--r--src/ipset_hash_ipport.c24
-rw-r--r--src/ipset_hash_ipportip.c20
-rw-r--r--src/ipset_hash_ipportnet.c20
-rw-r--r--tests/hash:ip,port,ip.t50
-rw-r--r--tests/hash:ip,port.t64
-rw-r--r--tests/hash:ip,port.t.list210
-rw-r--r--tests/hash:ip6,port,ip6.t50
-rw-r--r--tests/hash:ip6,port.t42
-rw-r--r--tests/hash:net.t36
-rw-r--r--tests/hash:net6.t36
-rw-r--r--tests/init.t2
-rw-r--r--tests/ipmap.t18
-rw-r--r--tests/ipporthash.t70
-rw-r--r--tests/ipportiphash.t80
-rw-r--r--tests/nethash.t36
-rw-r--r--tests/setlist.t54
44 files changed, 1181 insertions, 577 deletions
diff --git a/Make_global.am b/Make_global.am
index 857d0de..5b2ec6f 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -38,12 +38,11 @@ AM_CFLAGS = -std=gnu99 \
-Wundef \
-Wwrite-strings \
-Wno-missing-field-initializers \
- -Werror \
- -g -ggdb -gdwarf-2 -g3
+ -Werror
endif
if ENABLE_DEBUG
-AM_CFLAGS += -DIPSET_DEBUG
+AM_CFLAGS += -g -ggdb -gdwarf-2 -g3 -DIPSET_DEBUG
endif
if ! ENABLE_VERBOSE
diff --git a/include/libipset/data.h b/include/libipset/data.h
index 936b807..cb35393 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_PROTO,
/* Swap/rename to */
IPSET_OPT_SETNAME2,
/* Flags */
@@ -93,6 +94,7 @@ enum ipset_opt {
| IPSET_FLAG(IPSET_OPT_NAMEREF) \
| IPSET_FLAG(IPSET_OPT_IP2) \
| IPSET_FLAG(IPSET_OPT_CIDR2) \
+ | IPSET_FLAG(IPSET_OPT_PROTO) \
| IPSET_FLAG(IPSET_OPT_CADT_FLAGS)\
| IPSET_FLAG(IPSET_OPT_BEFORE))
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index 4af75ba..0ad2b14 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -20,47 +20,47 @@
/* Message types and commands */
enum ipset_cmd {
IPSET_CMD_NONE,
- IPSET_CMD_CREATE, /* Create a new (empty) set */
- IPSET_CMD_DESTROY, /* Remove a (empty) set */
- IPSET_CMD_FLUSH, /* Remove all elements from a set */
- IPSET_CMD_RENAME, /* Rename a set */
- IPSET_CMD_SWAP, /* Swap two sets */
- IPSET_CMD_LIST, /* List sets */
- IPSET_CMD_SAVE, /* Save sets */
- IPSET_CMD_ADD, /* Add an element to a set */
- IPSET_CMD_DEL, /* Delete an element from a set */
- IPSET_CMD_TEST, /* Test an element in a set */
- IPSET_CMD_HEADER, /* Get set header data only */
- IPSET_CMD_TYPE, /* Get set type */
- IPSET_CMD_PROTOCOL, /* Return protocol version */
+ IPSET_CMD_CREATE, /* 1: Create a new (empty) set */
+ IPSET_CMD_DESTROY, /* 2: Remove a (empty) set */
+ IPSET_CMD_FLUSH, /* 3: Remove all elements from a set */
+ IPSET_CMD_RENAME, /* 4: Rename a set */
+ IPSET_CMD_SWAP, /* 5: Swap two sets */
+ IPSET_CMD_LIST, /* 6: List sets */
+ IPSET_CMD_SAVE, /* 7: Save sets */
+ IPSET_CMD_ADD, /* 8: Add an element to a set */
+ IPSET_CMD_DEL, /* 9: Delete an element from a set */
+ IPSET_CMD_TEST, /* 10: Test an element in a set */
+ IPSET_CMD_HEADER, /* 11: Get set header data only */
+ IPSET_CMD_TYPE, /* 12: Get set type */
+ IPSET_CMD_PROTOCOL, /* 13: Return protocol version */
IPSET_MSG_MAX, /* Netlink message commands */
/* Commands in userspace: */
- IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */
- IPSET_CMD_HELP, /* Get help */
- IPSET_CMD_VERSION, /* Get program version */
- IPSET_CMD_QUIT, /* Quit from interactive mode */
+ IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
+ IPSET_CMD_HELP, /* 15: Get help */
+ IPSET_CMD_VERSION, /* 16: Get program version */
+ IPSET_CMD_QUIT, /* 17: Quit from interactive mode */
IPSET_CMD_MAX,
- IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */
+ IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
};
/* Attributes at command level */
enum {
IPSET_ATTR_UNSPEC,
- IPSET_ATTR_PROTOCOL, /* Protocol version */
- IPSET_ATTR_SETNAME, /* Name of the set */
- IPSET_ATTR_TYPENAME, /* Typename */
+ IPSET_ATTR_PROTOCOL, /* 1: Protocol version */
+ IPSET_ATTR_SETNAME, /* 2: Name of the set */
+ IPSET_ATTR_TYPENAME, /* 3: Typename */
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
- IPSET_ATTR_REVISION, /* Settype revision */
- IPSET_ATTR_FAMILY, /* Settype family */
- IPSET_ATTR_FLAGS, /* Flags at command level */
- IPSET_ATTR_DATA, /* Nested attributes */
- IPSET_ATTR_ADT, /* Multiple data containers */
- IPSET_ATTR_LINENO, /* Restore lineno */
- IPSET_ATTR_PROTOCOL_MIN,/* Minimal supported version number */
- IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
+ IPSET_ATTR_REVISION, /* 4: Settype revision */
+ IPSET_ATTR_FAMILY, /* 5: Settype family */
+ IPSET_ATTR_FLAGS, /* 6: Flags at command level */
+ IPSET_ATTR_DATA, /* 7: Nested attributes */
+ IPSET_ATTR_ADT, /* 8: Multiple data containers */
+ IPSET_ATTR_LINENO, /* 9: Restore lineno */
+ IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
+ IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
__IPSET_ATTR_CMD_MAX,
};
#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1)
@@ -69,13 +69,14 @@ enum {
enum {
IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
- IPSET_ATTR_IP_TO,
- IPSET_ATTR_CIDR,
- IPSET_ATTR_PORT,
+ IPSET_ATTR_IP_TO, /* 2 */
+ IPSET_ATTR_CIDR, /* 3 */
+ IPSET_ATTR_PORT, /* 4 */
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
- IPSET_ATTR_PORT_TO,
- IPSET_ATTR_TIMEOUT,
- IPSET_ATTR_CADT_FLAGS,
+ IPSET_ATTR_PORT_TO, /* 5 */
+ IPSET_ATTR_TIMEOUT, /* 6 */
+ IPSET_ATTR_PROTO, /* 7 */
+ IPSET_ATTR_CADT_FLAGS, /* 8 */
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
@@ -147,4 +148,7 @@ enum ipset_adt {
IPSET_CADT_MAX,
};
+#define IPSET_IPPROTO_ANY 255
+#define IPSET_IPPROTO_TCPUDP 254
+
#endif /* __IP_SET_H */
diff --git a/include/libipset/linux_ip_set_hash.h b/include/libipset/linux_ip_set_hash.h
index 76d2489..db6977b 100644
--- a/include/libipset/linux_ip_set_hash.h
+++ b/include/libipset/linux_ip_set_hash.h
@@ -5,6 +5,8 @@
enum {
IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC,
IPSET_ERR_HASH_ELEM,
+ IPSET_ERR_INVALID_PROTO,
+ IPSET_ERR_MISSING_PROTO,
};
#endif /* __IP_SET_HASH_H */
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
index 143e2b3..1e055ee 100644
--- a/include/libipset/parse.h
+++ b/include/libipset/parse.h
@@ -14,6 +14,7 @@
#define IPSET_RANGE_SEPARATOR "-"
#define IPSET_ELEM_SEPARATOR ","
#define IPSET_NAME_SEPARATOR ","
+#define IPSET_PROTO_SEPARATOR ":"
struct ipset_session;
@@ -26,6 +27,10 @@ extern int ipset_parse_single_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_port(struct ipset_session *session,
enum ipset_opt opt, const char *str);
+extern int ipset_parse_proto(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
+extern int ipset_parse_proto_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str);
extern int ipset_parse_family(struct ipset_session *session,
enum ipset_opt opt, const char *str);
extern int ipset_parse_ip(struct ipset_session *session,
diff --git a/include/libipset/print.h b/include/libipset/print.h
index dbb70f3..1fc5abc 100644
--- a/include/libipset/print.h
+++ b/include/libipset/print.h
@@ -37,6 +37,12 @@ extern int ipset_print_name(char *buf, unsigned int len,
extern int ipset_print_port(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env);
+extern int ipset_print_proto(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env);
+extern int ipset_print_proto_port(char *buf, unsigned int len,
+ const struct ipset_data *data,
+ enum ipset_opt opt, uint8_t env);
extern int ipset_print_flag(char *buf, unsigned int len,
const struct ipset_data *data, enum ipset_opt opt,
uint8_t env);
diff --git a/include/libipset/session.h b/include/libipset/session.h
index cc0940e..02e8b36 100644
--- a/include/libipset/session.h
+++ b/include/libipset/session.h
@@ -23,6 +23,7 @@ struct ipset_handle;
extern struct ipset_data * ipset_session_data(const struct ipset_session *session);
extern struct ipset_handle * ipset_session_handle(const struct ipset_session *session);
+extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session);
enum ipset_err_type {
IPSET_ERROR,
diff --git a/kernel/include/linux/netfilter/ip_set.h b/kernel/include/linux/netfilter/ip_set.h
index e700503..f306859 100644
--- a/kernel/include/linux/netfilter/ip_set.h
+++ b/kernel/include/linux/netfilter/ip_set.h
@@ -20,47 +20,47 @@
/* Message types and commands */
enum ipset_cmd {
IPSET_CMD_NONE,
- IPSET_CMD_CREATE, /* Create a new (empty) set */
- IPSET_CMD_DESTROY, /* Remove a (empty) set */
- IPSET_CMD_FLUSH, /* Remove all elements from a set */
- IPSET_CMD_RENAME, /* Rename a set */
- IPSET_CMD_SWAP, /* Swap two sets */
- IPSET_CMD_LIST, /* List sets */
- IPSET_CMD_SAVE, /* Save sets */
- IPSET_CMD_ADD, /* Add an element to a set */
- IPSET_CMD_DEL, /* Delete an element from a set */
- IPSET_CMD_TEST, /* Test an element in a set */
- IPSET_CMD_HEADER, /* Get set header data only */
- IPSET_CMD_TYPE, /* Get set type */
- IPSET_CMD_PROTOCOL, /* Return protocol version */
+ IPSET_CMD_CREATE, /* 1: Create a new (empty) set */
+ IPSET_CMD_DESTROY, /* 2: Remove a (empty) set */
+ IPSET_CMD_FLUSH, /* 3: Remove all elements from a set */
+ IPSET_CMD_RENAME, /* 4: Rename a set */
+ IPSET_CMD_SWAP, /* 5: Swap two sets */
+ IPSET_CMD_LIST, /* 6: List sets */
+ IPSET_CMD_SAVE, /* 7: Save sets */
+ IPSET_CMD_ADD, /* 8: Add an element to a set */
+ IPSET_CMD_DEL, /* 9: Delete an element from a set */
+ IPSET_CMD_TEST, /* 10: Test an element in a set */
+ IPSET_CMD_HEADER, /* 11: Get set header data only */
+ IPSET_CMD_TYPE, /* 12: Get set type */
+ IPSET_CMD_PROTOCOL, /* 13: Return protocol version */
IPSET_MSG_MAX, /* Netlink message commands */
/* Commands in userspace: */
- IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* Enter restore mode */
- IPSET_CMD_HELP, /* Get help */
- IPSET_CMD_VERSION, /* Get program version */
- IPSET_CMD_QUIT, /* Quit from interactive mode */
+ IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
+ IPSET_CMD_HELP, /* 15: Get help */
+ IPSET_CMD_VERSION, /* 16: Get program version */
+ IPSET_CMD_QUIT, /* 17: Quit from interactive mode */
IPSET_CMD_MAX,
- IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* Commit buffered commands */
+ IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
};
/* Attributes at command level */
enum {
IPSET_ATTR_UNSPEC,
- IPSET_ATTR_PROTOCOL, /* Protocol version */
- IPSET_ATTR_SETNAME, /* Name of the set */
- IPSET_ATTR_TYPENAME, /* Typename */
+ IPSET_ATTR_PROTOCOL, /* 1: Protocol version */
+ IPSET_ATTR_SETNAME, /* 2: Name of the set */
+ IPSET_ATTR_TYPENAME, /* 3: Typename */
IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* rename/swap */
- IPSET_ATTR_REVISION, /* Settype revision */
- IPSET_ATTR_FAMILY, /* Settype family */
- IPSET_ATTR_FLAGS, /* Flags at command level */
- IPSET_ATTR_DATA, /* Nested attributes */
- IPSET_ATTR_ADT, /* Multiple data containers */
- IPSET_ATTR_LINENO, /* Restore lineno */
- IPSET_ATTR_PROTOCOL_MIN,/* Minimal supported version number */
- IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
+ IPSET_ATTR_REVISION, /* 4: Settype revision */
+ IPSET_ATTR_FAMILY, /* 5: Settype family */
+ IPSET_ATTR_FLAGS, /* 6: Flags at command level */
+ IPSET_ATTR_DATA, /* 7: Nested attributes */
+ IPSET_ATTR_ADT, /* 8: Multiple data containers */
+ IPSET_ATTR_LINENO, /* 9: Restore lineno */
+ IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
+ IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
__IPSET_ATTR_CMD_MAX,
};
#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1)
@@ -69,13 +69,14 @@ enum {
enum {
IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
- IPSET_ATTR_IP_TO,
- IPSET_ATTR_CIDR,
- IPSET_ATTR_PORT,
+ IPSET_ATTR_IP_TO, /* 2 */
+ IPSET_ATTR_CIDR, /* 3 */
+ IPSET_ATTR_PORT, /* 4 */
IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
- IPSET_ATTR_PORT_TO,
- IPSET_ATTR_TIMEOUT,
- IPSET_ATTR_CADT_FLAGS,
+ IPSET_ATTR_PORT_TO, /* 5 */
+ IPSET_ATTR_TIMEOUT, /* 6 */
+ IPSET_ATTR_PROTO, /* 7 */
+ IPSET_ATTR_CADT_FLAGS, /* 8 */
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,
/* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16,
@@ -147,6 +148,9 @@ enum ipset_adt {
IPSET_CADT_MAX,
};
+#define IPSET_IPPROTO_ANY 255
+#define IPSET_IPPROTO_TCPUDP 254
+
#ifdef __KERNEL__
#include <linux/ip.h>
#include <linux/ipv6.h>
@@ -343,6 +347,12 @@ ip_set_free(void *members, u8 flags)
vfree(members);
}
+static inline bool
+ip_set_eexist(int ret, u32 flags)
+{
+ return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST);
+}
+
/* Useful converters */
static inline u32
ip_set_get_h32(const struct nlattr *attr)
diff --git a/kernel/include/linux/netfilter/ip_set_chash.h b/kernel/include/linux/netfilter/ip_set_chash.h
index 0d77a5d..e0e16bd 100644
--- a/kernel/include/linux/netfilter/ip_set_chash.h
+++ b/kernel/include/linux/netfilter/ip_set_chash.h
@@ -37,6 +37,9 @@ struct chash {
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */
#endif
+#ifdef IP_SET_HASH_WITH_PROTO
+ u8 proto; /* default protocol for SET target */
+#endif
#ifdef IP_SET_HASH_WITH_NETS
struct chash_nets nets[0]; /* book keeping of networks */
#endif
@@ -205,8 +208,9 @@ jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \
/* Flavour without timeout */
-#define chash_data(n, i) \
-(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) + (i)*sizeof(struct type_pf_elem))
+#define chash_data(n, i) \
+(struct type_pf_elem *)((char *)(n) + sizeof(struct slist) \
+ + (i)*sizeof(struct type_pf_elem))
static int
type_pf_chash_readd(struct chash *h, struct slist *t, u8 htable_bits,
@@ -507,6 +511,10 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb)
if (h->netmask != HOST_MASK)
NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask);
#endif
+#ifdef IP_SET_HASH_WITH_PROTO
+ if (h->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, h->proto);
+#endif
NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
htonl(atomic_read(&set->ref) - 1));
NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize));
diff --git a/kernel/include/linux/netfilter/ip_set_getport.h b/kernel/include/linux/netfilter/ip_set_getport.h
index ffa89f1..680a89a 100644
--- a/kernel/include/linux/netfilter/ip_set_getport.h
+++ b/kernel/include/linux/netfilter/ip_set_getport.h
@@ -8,72 +8,91 @@
#define IPSET_INVALID_PORT 65536
/* We must handle non-linear skbs */
-static bool
-get_port(u8 pf, const struct sk_buff *skb, bool src, u16 *port)
-{
- unsigned short protocol;
- unsigned int protoff;
- int fragoff;
-
- switch (pf) {
- case AF_INET: {
- const struct iphdr *iph = ip_hdr(skb);
-
- protocol = iph->protocol;
- fragoff = ntohs(iph->frag_off) & IP_OFFSET;
- protoff = ip_hdrlen(skb);
- break;
- }
- case AF_INET6: {
- int protohdr;
- unsigned short frag_off;
-
- protohdr = ipv6_find_hdr(skb, &protoff, -1, &frag_off);
- if (protohdr < 0)
- return false;
-
- protocol = protohdr;
- fragoff = frag_off;
- break;
- }
- default:
- return false;
- }
-
- /* See comments at tcp_match in ip_tables.c */
- if (fragoff)
- return false;
+static inline bool
+get_port(const struct sk_buff *skb, int protocol, unsigned int protooff,
+ bool src, u16 *port, u8 *proto)
+{
switch (protocol) {
case IPPROTO_TCP: {
struct tcphdr _tcph;
const struct tcphdr *th;
- th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
+ th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph);
if (th == NULL)
/* No choice either */
return false;
*port = src ? th->source : th->dest;
break;
- }
+ }
case IPPROTO_UDP: {
struct udphdr _udph;
const struct udphdr *uh;
- uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
+ uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph);
if (uh == NULL)
/* No choice either */
return false;
*port = src ? uh->source : uh->dest;
break;
- }
+ }
default:
- return false;
+ if (*proto == IPSET_IPPROTO_TCPUDP)
+ return false;
+ break;
}
+ if (*proto != IPSET_IPPROTO_TCPUDP)
+ *proto = protocol;
+
return true;
}
+
+static inline bool
+get_ip4_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto)
+{
+ const struct iphdr *iph = ip_hdr(skb);
+ unsigned int protooff = ip_hdrlen(skb);
+ int protocol = iph->protocol;
+
+ if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol))
+ return false;
+
+ /* See comments at tcp_match in ip_tables.c */
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ return false;
+
+ return get_port(skb, protocol, protooff, src, port, proto);
+}
+
+static inline bool
+get_ip6_port(const struct sk_buff *skb, bool src, u16 *port, u8 *proto)
+{
+ unsigned int *protooff = 0;
+ int protocol;
+ unsigned short fragoff;
+
+ protocol = ipv6_find_hdr(skb, protooff, -1, &fragoff);
+ if (protocol < 0 || fragoff)
+ return false;
+
+ if (!(*proto >= IPSET_IPPROTO_TCPUDP || *proto == protocol))
+ return false;
+
+ return get_port(skb, protocol, *protooff, src, port, proto);
+}
+
+static inline bool
+get_ip_port(const struct sk_buff *skb, u8 pf, bool src, u16 *port)
+{
+ u8 proto = IPSET_IPPROTO_TCPUDP;
+
+ if (pf == AF_INET)
+ return get_ip4_port(skb, src, port, &proto);
+ else
+ return get_ip6_port(skb, src, port, &proto);
+}
#endif /* __KERNEL__ */
#endif /*_IP_SET_GETPORT_H*/
diff --git a/kernel/include/linux/netfilter/ip_set_hash.h b/kernel/include/linux/netfilter/ip_set_hash.h
index c1a6964..4003af0 100644
--- a/kernel/include/linux/netfilter/ip_set_hash.h
+++ b/kernel/include/linux/netfilter/ip_set_hash.h
@@ -5,6 +5,8 @@
enum {
IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC,
IPSET_ERR_HASH_ELEM,
+ IPSET_ERR_INVALID_PROTO,
+ IPSET_ERR_MISSING_PROTO,
};
#ifdef __KERNEL__
diff --git a/kernel/ip_set.c b/kernel/ip_set.c
index 5bf331e..9d7093c 100644
--- a/kernel/ip_set.c
+++ b/kernel/ip_set.c
@@ -474,7 +474,6 @@ ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] __read_mostly = {
.len = IPSET_MAXNAMELEN - 1},
[IPSET_ATTR_REVISION] = { .type = NLA_U8 },
[IPSET_ATTR_FAMILY] = { .type = NLA_U8 },
- [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_DATA] = { .type = NLA_NESTED },
};
diff --git a/kernel/ip_set_bitmap_ip.c b/kernel/ip_set_bitmap_ip.c
index 66d3979..27b9665 100644
--- a/kernel/ip_set_bitmap_ip.c
+++ b/kernel/ip_set_bitmap_ip.c
@@ -107,6 +107,7 @@ bitmap_ip_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
[IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -115,7 +116,6 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct bitmap_ip *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
u32 ip, ip_to, id;
int ret = 0;
@@ -123,6 +123,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
bitmap_ip_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
else
@@ -164,11 +167,10 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adt == IPSET_ADD ? bitmap_ip_add(map, id)
: bitmap_ip_del(map, id);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ if (ret && !ip_set_eexist(ret, flags))
return ret;
- }
+ else
+ ret = 0;
}
return ret;
}
@@ -356,7 +358,6 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct bitmap_ip_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
u32 ip, ip_to, id, timeout = map->timeout;
int ret = 0;
@@ -364,6 +365,9 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
bitmap_ip_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
ip = ip_set_get_h32(tb[IPSET_ATTR_IP]);
else
@@ -405,11 +409,10 @@ bitmap_ip_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
? bitmap_ip_timeout_add(map, id, timeout)
: bitmap_ip_timeout_del(map, id);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ if (ret && !ip_set_eexist(ret, flags))
return ret;
- }
+ else
+ ret = 0;
}
return ret;
}
diff --git a/kernel/ip_set_bitmap_ipmac.c b/kernel/ip_set_bitmap_ipmac.c
index d036862..3c94975 100644
--- a/kernel/ip_set_bitmap_ipmac.c
+++ b/kernel/ip_set_bitmap_ipmac.c
@@ -368,6 +368,7 @@ bitmap_ipmac_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_U32 },
[IPSET_ATTR_ETHER] = { .type = NLA_BINARY, .len = ETH_ALEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -377,7 +378,6 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
struct bitmap_ipmac *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
ipset_adtfn adtfn = set->variant->adt[adt];
- bool eexist = flags & IPSET_FLAG_EXIST;
struct ipmac data;
u32 timeout = map->timeout;
int ret = 0;
@@ -386,6 +386,9 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
bitmap_ipmac_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
data.id = ip_set_get_h32(tb[IPSET_ATTR_IP]);
else
@@ -409,11 +412,7 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adtfn(set, &data, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static void
diff --git a/kernel/ip_set_bitmap_port.c b/kernel/ip_set_bitmap_port.c
index f3e498a..6517252 100644
--- a/kernel/ip_set_bitmap_port.c
+++ b/kernel/ip_set_bitmap_port.c
@@ -72,9 +72,9 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port *map = set->data;
- u16 port;
+ u16 port = 0;
- if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port))
+ if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port))
return -EINVAL;
port = ntohs(port);
@@ -101,6 +101,7 @@ bitmap_port_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_PORT_TO] = { .type = NLA_U16 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -109,7 +110,6 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct bitmap_port *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
u32 port; /* wraparound */
u16 id, port_to;
int ret = 0;
@@ -118,6 +118,9 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
bitmap_port_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_PORT])
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
else
@@ -150,11 +153,10 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adt == IPSET_ADD ? bitmap_port_add(map, id)
: bitmap_port_del(map, id);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ if (ret && !ip_set_eexist(ret, flags))
return ret;
- }
+ else
+ ret = 0;
}
return ret;
}
@@ -313,9 +315,9 @@ bitmap_port_timeout_kadt(struct ip_set *set, const struct sk_buff *skb,
enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
struct bitmap_port_timeout *map = set->data;
- u16 port;
+ u16 port = 0;
- if (!get_port(pf, skb, flags & IPSET_DIM_ONE_SRC, &port))
+ if (!get_ip_port(skb, pf, flags & IPSET_DIM_ONE_SRC, &port))
return -EINVAL;
port = ntohs(port);
@@ -343,7 +345,6 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
{
const struct bitmap_port_timeout *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
u16 id, port_to;
u32 port, timeout = map->timeout; /* wraparound */
int ret = 0;
@@ -352,6 +353,9 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
bitmap_port_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_PORT])
port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
else
@@ -385,11 +389,10 @@ bitmap_port_timeout_uadt(struct ip_set *set, struct nlattr *head, int len,
? bitmap_port_timeout_add(map, id, timeout)
: bitmap_port_timeout_del(map, id);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ if (ret && !ip_set_eexist(ret, flags))
return ret;
- }
+ else
+ ret = 0;
}
return ret;
}
diff --git a/kernel/ip_set_hash_ip.c b/kernel/ip_set_hash_ip.c
index e5ce6a4..d73a5da 100644
--- a/kernel/ip_set_hash_ip.c
+++ b/kernel/ip_set_hash_ip.c
@@ -140,6 +140,7 @@ hash_ip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP_TO] = { .type = NLA_U32 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -148,7 +149,6 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
ipset_adtfn adtfn = set->variant->adt[adt];
u32 ip, nip, ip_to, hosts, timeout = h->timeout;
int ret = 0;
@@ -157,6 +157,9 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
hash_ip4_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
else
@@ -196,11 +199,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *head, int len,
nip = htonl(ip);
ret = adtfn(set, &nip, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+ if (ret && !ip_set_eexist(ret, flags))
return ret;
- }
+ else
+ ret = 0;
}
return ret;
}
@@ -213,10 +215,10 @@ hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
+ && x->netmask == y->netmask
&& x->htable_bits == y->htable_bits /* resizing ? */
&& x->array_size == y->array_size
- && x->chain_limit == y->chain_limit
- && x->netmask == y->netmask;
+ && x->chain_limit == y->chain_limit;
}
/* The type variant functions: IPv6 */
@@ -327,6 +329,7 @@ hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -338,11 +341,15 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
ipset_adtfn adtfn = set->variant->adt[adt];
union nf_inet_addr *ip;
u32 timeout = h->timeout;
+ int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ip6_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
ip = nla_data(tb[IPSET_ATTR_IP]);
else
@@ -358,7 +365,9 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- return adtfn(set, ip, GFP_KERNEL, timeout);
+ ret = adtfn(set, ip, GFP_KERNEL, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
diff --git a/kernel/ip_set_hash_ipport.c b/kernel/ip_set_hash_ipport.c
index 8210f67..cb319d2 100644
--- a/kernel/ip_set_hash_ipport.c
+++ b/kernel/ip_set_hash_ipport.c
@@ -48,14 +48,16 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
struct hash_ipport4_elem {
u32 ip;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
};
/* Member elements with timeout support */
struct hash_ipport4_telem {
u32 ip;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
unsigned long timeout;
};
@@ -63,13 +65,15 @@ static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2)
{
- return ip1->ip == ip2->ip && ip1->port == ip2->port;
+ return ip1->ip == ip2->ip
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -78,7 +82,7 @@ hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
{
dst->ip = src->ip;
dst->port = src->port;
- dst->match = 1;
+ dst->proto = src->proto;
}
static inline void
@@ -87,12 +91,13 @@ hash_ipport4_data_swap(struct hash_ipport4_elem *dst,
{
swap(dst->ip, src->ip);
swap(dst->port, src->port);
+ swap(dst->proto, src->proto);
}
static inline void
hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline bool
@@ -101,6 +106,8 @@ hash_ipport4_data_list(struct sk_buff *skb,
{
NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -116,6 +123,8 @@ hash_ipport4_data_tlist(struct sk_buff *skb,
NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
@@ -125,6 +134,8 @@ nla_put_failure:
return 1;
}
+#define IP_SET_HASH_WITH_PROTO
+
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ip_set_chash.h>
@@ -135,12 +146,14 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport4_elem data = {};
+ struct hash_ipport4_elem data = { .proto = h->proto };
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip4_port(skb, flags & IPSET_DIM_ONE_SRC,
+ &data.port, &data.proto))
return -EINVAL;
+ ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
+
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
@@ -148,7 +161,9 @@ static const struct nla_policy
hash_ipport4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_U32 },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -157,9 +172,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport4_elem data = {};
+ struct hash_ipport4_elem data = { .proto = h->proto };
u32 timeout = h->timeout;
int ret;
@@ -167,6 +181,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
hash_ipport4_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
else
@@ -177,6 +194,24 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
@@ -185,11 +220,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adtfn(set, &data, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static bool
@@ -200,6 +231,7 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
+ && x->proto == y->proto
&& x->htable_bits == y->htable_bits /* resizing ? */
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
@@ -210,13 +242,15 @@ hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
struct hash_ipport6_elem {
union nf_inet_addr ip;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
};
struct hash_ipport6_telem {
union nf_inet_addr ip;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
unsigned long timeout;
};
@@ -225,13 +259,14 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2)
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
- && ip1->port == ip2->port;
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -239,7 +274,6 @@ hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
const struct hash_ipport6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
- dst->match = 1;
}
static inline void
@@ -256,7 +290,7 @@ hash_ipport6_data_swap(struct hash_ipport6_elem *dst,
static inline void
hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline bool
@@ -265,6 +299,8 @@ hash_ipport6_data_list(struct sk_buff *skb,
{
NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -280,6 +316,8 @@ hash_ipport6_data_tlist(struct sk_buff *skb,
NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
return 0;
@@ -301,12 +339,14 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport6_elem data = {};
+ struct hash_ipport6_elem data = { .proto = h->proto };
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip6_port(skb, flags & IPSET_DIM_ONE_SRC,
+ &data.port, &data.proto))
return -EINVAL;
+ ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
+
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
}
@@ -315,7 +355,9 @@ hash_ipport6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -325,13 +367,17 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipport6_elem data = {};
+ struct hash_ipport6_elem data = { .proto = h->proto };
u32 timeout = h->timeout;
+ int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipport6_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
sizeof(struct in6_addr));
@@ -343,13 +389,33 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- return adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
@@ -360,6 +426,7 @@ hash_ipport_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
@@ -367,8 +434,9 @@ static int
hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct chash *h;
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
@@ -386,6 +454,12 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+ if (tb[IPSET_ATTR_PROTO]) {
+ proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+ if (!proto)
+ return -IPSET_ERR_INVALID_PROTO;
+ }
+
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -395,6 +469,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *head, int len, u32 flags)
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
+ h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
diff --git a/kernel/ip_set_hash_ipportip.c b/kernel/ip_set_hash_ipportip.c
index fbf2780..2c3cf9b 100644
--- a/kernel/ip_set_hash_ipportip.c
+++ b/kernel/ip_set_hash_ipportip.c
@@ -49,7 +49,8 @@ struct hash_ipportip4_elem {
u32 ip;
u32 ip2;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
};
/* Member elements with timeout support */
@@ -57,7 +58,8 @@ struct hash_ipportip4_telem {
u32 ip;
u32 ip2;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
unsigned long timeout;
};
@@ -67,13 +69,14 @@ hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
{
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
- && ip1->port == ip2->port;
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -81,7 +84,6 @@ hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
const struct hash_ipportip4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
- dst->match = 1;
}
static inline void
@@ -98,7 +100,7 @@ hash_ipportip4_data_swap(struct hash_ipportip4_elem *dst,
static inline void
hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline bool
@@ -108,6 +110,8 @@ hash_ipportip4_data_list(struct sk_buff *skb,
NLA_PUT_NET32(skb, IPSET_ATTR_IP, data->ip);
NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -124,6 +128,8 @@ hash_ipportip4_data_tlist(struct sk_buff *skb,
NLA_PUT_NET32(skb, IPSET_ATTR_IP, tdata->ip);
NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
@@ -133,6 +139,8 @@ nla_put_failure:
return 1;
}
+#define IP_SET_HASH_WITH_PROTO
+
#define PF 4
#define HOST_MASK 32
#include <linux/netfilter/ip_set_chash.h>
@@ -143,11 +151,13 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip4_elem data = {};
+ struct hash_ipportip4_elem data = { .proto = h->proto };
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip4_port(skb, 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);
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
@@ -158,7 +168,9 @@ hash_ipportip4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_U32 },
[IPSET_ATTR_IP2] = { .type = NLA_U32 },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -167,9 +179,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip4_elem data = {};
+ struct hash_ipportip4_elem data = { .proto = h->proto };
u32 timeout = h->timeout;
int ret;
@@ -177,6 +188,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
hash_ipportip4_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
else
@@ -192,6 +206,24 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
@@ -200,11 +232,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adtfn(set, &data, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static bool
@@ -215,6 +243,7 @@ hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
+ && x->proto == y->proto
&& x->htable_bits == y->htable_bits /* resizing ? */
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
@@ -226,14 +255,16 @@ struct hash_ipportip6_elem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
};
struct hash_ipportip6_telem {
union nf_inet_addr ip;
union nf_inet_addr ip2;
u16 port;
- u16 match;
+ u8 proto;
+ u8 padding;
unsigned long timeout;
};
@@ -243,13 +274,14 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
{
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
- && ip1->port == ip2->port;
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -257,7 +289,6 @@ hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
const struct hash_ipportip6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
- dst->match = 1;
}
static inline void
@@ -274,7 +305,7 @@ hash_ipportip6_data_swap(struct hash_ipportip6_elem *dst,
static inline void
hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline bool
@@ -284,6 +315,8 @@ hash_ipportip6_data_list(struct sk_buff *skb,
NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &data->ip);
NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -300,6 +333,8 @@ hash_ipportip6_data_tlist(struct sk_buff *skb,
NLA_PUT(skb, IPSET_ATTR_IP, sizeof(struct in6_addr), &e->ip);
NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
return 0;
@@ -321,11 +356,13 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
{
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip6_elem data = {};
+ struct hash_ipportip6_elem data = { .proto = h->proto };
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip6_port(skb, 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);
return adtfn(set, &data, GFP_ATOMIC, h->timeout);
@@ -338,7 +375,9 @@ hash_ipportip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP2] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -348,13 +387,17 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportip6_elem data = {};
+ struct hash_ipportip6_elem data = { .proto = h->proto };
u32 timeout = h->timeout;
+ int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportip6_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
sizeof(struct in6_addr));
@@ -372,13 +415,33 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- return adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
@@ -389,6 +452,7 @@ hash_ipportip_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
@@ -397,8 +461,9 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct chash *h;
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
@@ -416,6 +481,12 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+ if (tb[IPSET_ATTR_PROTO]) {
+ proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+ if (!proto)
+ return -IPSET_ERR_INVALID_PROTO;
+ }
+
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -425,6 +496,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *head,
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
+ h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
diff --git a/kernel/ip_set_hash_ipportnet.c b/kernel/ip_set_hash_ipportnet.c
index dfe9348..4bc44f5 100644
--- a/kernel/ip_set_hash_ipportnet.c
+++ b/kernel/ip_set_hash_ipportnet.c
@@ -50,7 +50,7 @@ struct hash_ipportnet4_elem {
u32 ip2;
u16 port;
u8 cidr;
- u8 match;
+ u8 proto;
};
/* Member elements with timeout support */
@@ -59,7 +59,7 @@ struct hash_ipportnet4_telem {
u32 ip2;
u16 port;
u8 cidr;
- u8 match;
+ u8 proto;
unsigned long timeout;
};
@@ -70,13 +70,14 @@ hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
return ip1->ip == ip2->ip
&& ip1->ip2 == ip2->ip2
&& ip1->cidr == ip2->cidr
- && ip1->port == ip2->port;
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -84,7 +85,6 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
const struct hash_ipportnet4_elem *src)
{
memcpy(dst, src, sizeof(*dst));
- dst->match = 1;
}
static inline void
@@ -108,7 +108,7 @@ hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
static inline void
hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline bool
@@ -119,6 +119,8 @@ hash_ipportnet4_data_list(struct sk_buff *skb,
NLA_PUT_NET32(skb, IPSET_ATTR_IP2, data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -136,6 +138,8 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb,
NLA_PUT_NET32(skb, IPSET_ATTR_IP2, tdata->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(tdata->timeout)));
@@ -145,6 +149,7 @@ nla_put_failure:
return 1;
}
+#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define PF 4
@@ -158,16 +163,19 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK };
-
+ { .cidr = h->nets[0].cidr || HOST_MASK,
+ .proto = h->proto };
+
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip4_port(skb, 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);
data.ip2 &= NETMASK(data.cidr);
@@ -180,7 +188,9 @@ hash_ipportnet4_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
[IPSET_ATTR_IP2] = { .type = NLA_U32 },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -189,9 +199,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet4_elem data = { .cidr = HOST_MASK };
+ struct hash_ipportnet4_elem data = { .cidr = HOST_MASK,
+ .proto = h->proto };
u32 timeout = h->timeout;
int ret;
@@ -199,6 +209,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
hash_ipportnet4_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
else
@@ -222,6 +235,24 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
@@ -230,11 +261,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adtfn(set, &data, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static bool
@@ -245,6 +272,7 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
return x->maxelem == y->maxelem
&& x->timeout == y->timeout
+ && x->proto == y->proto
&& x->htable_bits == y->htable_bits /* resizing ? */
&& x->array_size == y->array_size
&& x->chain_limit == y->chain_limit;
@@ -257,7 +285,7 @@ struct hash_ipportnet6_elem {
union nf_inet_addr ip2;
u16 port;
u8 cidr;
- u8 match;
+ u8 proto;
};
struct hash_ipportnet6_telem {
@@ -265,7 +293,7 @@ struct hash_ipportnet6_telem {
union nf_inet_addr ip2;
u16 port;
u8 cidr;
- u8 match;
+ u8 proto;
unsigned long timeout;
};
@@ -276,13 +304,14 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
&& ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0
&& ip1->cidr == ip2->cidr
- && ip1->port == ip2->port;
+ && ip1->port == ip2->port
+ && ip1->proto == ip2->proto;
}
static inline bool
hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
{
- return elem->match == 0;
+ return elem->proto == 0;
}
static inline void
@@ -290,7 +319,6 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
const struct hash_ipportnet6_elem *src)
{
memcpy(dst, src, sizeof(*dst));
- dst->match = 1;
}
static inline void
@@ -307,7 +335,7 @@ hash_ipportnet6_data_swap(struct hash_ipportnet6_elem *dst,
static inline void
hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
{
- elem->match = 0;
+ elem->proto = 0;
}
static inline void
@@ -334,6 +362,8 @@ hash_ipportnet6_data_list(struct sk_buff *skb,
NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
return 0;
nla_put_failure:
@@ -351,6 +381,8 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb,
NLA_PUT(skb, IPSET_ATTR_IP2, sizeof(struct in6_addr), &data->ip2);
NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port);
NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr);
+ if (data->proto != IPSET_IPPROTO_TCPUDP)
+ NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto);
NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
htonl(ip_set_timeout_get(e->timeout)));
return 0;
@@ -373,16 +405,19 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct chash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem data =
- { .cidr = h->nets[0].cidr || HOST_MASK };
+ { .cidr = h->nets[0].cidr || HOST_MASK,
+ .proto = h->proto };
if (data.cidr == 0)
return -EINVAL;
if (adt == IPSET_TEST)
data.cidr = HOST_MASK;
- ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
- if (!get_port(AF_INET, skb, flags & IPSET_DIM_TWO_SRC, &data.port))
+ if (!get_ip6_port(skb, 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);
ip6_netmask(&data.ip2, data.cidr);
@@ -397,7 +432,9 @@ hash_ipportnet6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
.len = sizeof(struct in6_addr) },
[IPSET_ATTR_PORT] = { .type = NLA_U16 },
[IPSET_ATTR_CIDR2] = { .type = NLA_U8 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -407,13 +444,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
ipset_adtfn adtfn = set->variant->adt[adt];
- struct hash_ipportnet6_elem data = { .cidr = HOST_MASK };
+ struct hash_ipportnet6_elem data = { .cidr = HOST_MASK,
+ .proto = h->proto };
u32 timeout = h->timeout;
+ int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_ipportnet6_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
sizeof(struct in6_addr));
@@ -439,13 +481,33 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *head, int len,
else
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_PROTO]) {
+ data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+
+ if (data.proto == 0 || data.proto >= IPSET_IPPROTO_TCPUDP)
+ return -IPSET_ERR_INVALID_PROTO;
+ } else if (data.proto == IPSET_IPPROTO_ANY)
+ return -IPSET_ERR_MISSING_PROTO;
+
+ switch (data.proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPSET_IPPROTO_TCPUDP:
+ break;
+ default:
+ data.port = 0;
+ break;
+ }
+
if (tb[IPSET_ATTR_TIMEOUT]) {
if (!with_timeout(h->timeout))
return -IPSET_ERR_TIMEOUT;
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- return adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
@@ -456,6 +518,7 @@ hash_ipportnet_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = {
[IPSET_ATTR_MAXELEM] = { .type = NLA_U32 },
[IPSET_ATTR_PROBES] = { .type = NLA_U8 },
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
+ [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
};
@@ -464,8 +527,9 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
int len, u32 flags)
{
struct nlattr *tb[IPSET_ATTR_CREATE_MAX];
- u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
struct chash *h;
+ u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+ u8 proto = IPSET_IPPROTO_TCPUDP; /* Backward compatibility */
if (!(set->family == AF_INET || set->family == AF_INET6))
return -IPSET_ERR_INVALID_FAMILY;
@@ -483,6 +547,11 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+ if (tb[IPSET_ATTR_PROTO]) {
+ proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+ if (!proto)
+ return -IPSET_ERR_INVALID_PROTO;
+ }
h = kzalloc(sizeof(*h)
+ sizeof(struct chash_nets)
* (set->family == AF_INET ? 31 : 127), GFP_KERNEL);
@@ -494,6 +563,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *head,
h->array_size = CHASH_DEFAULT_ARRAY_SIZE;
h->chain_limit = CHASH_DEFAULT_CHAIN_LIMIT;
get_random_bytes(&h->initval, sizeof(h->initval));
+ h->proto = proto;
h->timeout = IPSET_NO_TIMEOUT;
h->htable = ip_set_alloc(jhash_size(h->htable_bits) * sizeof(struct slist),
diff --git a/kernel/ip_set_hash_net.c b/kernel/ip_set_hash_net.c
index a8611c2..9be9e2c 100644
--- a/kernel/ip_set_hash_net.c
+++ b/kernel/ip_set_hash_net.c
@@ -46,13 +46,17 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
/* Member elements without timeout */
struct hash_net4_elem {
u32 ip;
- u8 cidr; /* Not hashed, zero for null value */
+ u16 padding0;
+ u8 padding1;
+ u8 cidr;
};
/* Member elements with timeout support */
struct hash_net4_telem {
u32 ip;
- u8 cidr; /* Not hashed, zero for null value */
+ u16 padding0;
+ u8 padding1;
+ u8 cidr;
unsigned long timeout;
};
@@ -60,10 +64,7 @@ static inline bool
hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2)
{
- /* We don't have to check the cidr equality
- * because overlapping nets cannot be added to the set
- */
- return ip1->ip == ip2->ip;
+ return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
}
static inline bool
@@ -168,7 +169,6 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct chash *h = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
@@ -178,6 +178,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
hash_net4_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
data.ip = ip_set_get_n32(tb[IPSET_ATTR_IP]);
else
@@ -199,11 +202,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *head, int len,
ret = adtfn(set, &data, GFP_KERNEL, timeout);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static bool
@@ -223,12 +222,16 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
struct hash_net6_elem {
union nf_inet_addr ip;
- u8 cidr; /* Not hashed */
+ u16 padding0;
+ u8 padding1;
+ u8 cidr;
};
struct hash_net6_telem {
union nf_inet_addr ip;
- u8 cidr; /* Not hashed */
+ u16 padding0;
+ u8 padding1;
+ u8 cidr;
unsigned long timeout;
};
@@ -236,7 +239,8 @@ static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2)
{
- return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
+ return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0
+ && ip1->cidr == ip2->cidr;
}
static inline bool
@@ -344,6 +348,7 @@ hash_net6_adt_policy[IPSET_ATTR_ADT_MAX + 1] __read_mostly = {
.len = sizeof(struct in6_addr) },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
};
static int
@@ -355,11 +360,15 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem data = { .cidr = HOST_MASK };
u32 timeout = h->timeout;
+ int ret;
if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len,
hash_net6_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_IP])
memcpy(&data.ip, nla_data(tb[IPSET_ATTR_IP]),
sizeof(struct in6_addr));
@@ -380,7 +389,9 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *head, int len,
timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
}
- return adtfn(set, &data, GFP_KERNEL, timeout);
+ ret = adtfn(set, &data, GFP_KERNEL, timeout);
+
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
/* Create hash:ip type of sets */
diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c
index ce6c4d1..94c5702 100644
--- a/kernel/ip_set_list_set.c
+++ b/kernel/ip_set_list_set.c
@@ -119,6 +119,7 @@ list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = {
[IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
.len = IPSET_MAXNAMELEN },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
};
@@ -210,8 +211,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
{
struct list_set *map = set->data;
struct nlattr *tb[IPSET_ATTR_ADT_MAX];
- bool eexist = flags & IPSET_FLAG_EXIST,
- with_timeout = with_timeout(map->timeout);
+ bool with_timeout = with_timeout(map->timeout);
int before = 0;
u32 timeout = map->timeout;
ip_set_id_t id, refid = IPSET_INVALID_ID;
@@ -224,6 +224,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *head, int len,
list_set_adt_policy))
return -IPSET_ERR_PROTOCOL;
+ if (tb[IPSET_ATTR_LINENO])
+ *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
+
if (tb[IPSET_ATTR_NAME]) {
id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
if (id == IPSET_INVALID_ID)
@@ -334,12 +337,7 @@ finish:
if (adt != IPSET_ADD || ret)
ip_set_put_byindex(id);
- if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) {
- if (tb[IPSET_ATTR_LINENO])
- *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
- return ret;
- }
- return ret;
+ return ip_set_eexist(ret, flags) ? 0 : ret;
}
static void
@@ -565,7 +563,7 @@ list_set_create(struct ip_set *set, struct nlattr *head, int len,
static struct ip_set_type list_set_type = {
.name = "list:set",
.protocol = IPSET_PROTOCOL,
- .features = IPSET_TYPE_NAME,
+ .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
.dimension = IPSET_DIM_ONE,
.family = AF_UNSPEC,
.revision = 0,
diff --git a/lib/data.c b/lib/data.c
index f8ff4a9..65ba209 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -66,6 +66,7 @@ struct ipset_data {
struct {
union nf_inet_addr ip2;
uint8_t cidr2;
+ uint8_t proto;
char ether[ETH_ALEN];
char name[IPSET_MAXNAMELEN];
char nameref[IPSET_MAXNAMELEN];
@@ -288,6 +289,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
case IPSET_OPT_CIDR2:
data->u.adt.cidr2 = *(const uint8_t *) value;
break;
+ case IPSET_OPT_PROTO:
+ data->u.adt.proto = *(const uint8_t *) value;
+ break;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
ipset_strncpy(data->u.setname2, value, IPSET_MAXNAMELEN);
@@ -396,6 +400,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
return &data->u.adt.ip2;
case IPSET_OPT_CIDR2:
return &data->u.adt.cidr2;
+ case IPSET_OPT_PROTO:
+ return &data->u.adt.proto;
/* Swap/rename */
case IPSET_OPT_SETNAME2:
return data->u.setname2;
@@ -450,6 +456,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
case IPSET_OPT_NETMASK:
case IPSET_OPT_PROBES:
case IPSET_OPT_RESIZE:
+ case IPSET_OPT_PROTO:
return sizeof(uint8_t);
case IPSET_OPT_ETHER:
return ETH_ALEN;
diff --git a/lib/parse.c b/lib/parse.c
index a07168d..b1fecc7 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -31,6 +31,7 @@
#define range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR)
#define elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR)
#define name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR)
+#define proto_separator(str) ipset_strchr(str, IPSET_PROTO_SEPARATOR)
#define syntax_err(fmt, args...) \
ipset_err(session, "Syntax error: " fmt , ## args)
@@ -281,6 +282,88 @@ error:
}
/**
+ * ipset_parse_proto - parse protocol name
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a protocol name. "any" is supported
+ * as a special protocol name for ipset itself.
+ * The parsed protocol are stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_proto(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint8_t proto = 0;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PROTO);
+ assert(str);
+
+ if (STREQ(str, "any"))
+ proto = IPSET_IPPROTO_ANY;
+ else {
+ struct protoent *protoent = getprotobyname(str);
+ if (protoent == NULL)
+ return syntax_err("cannot parse '%s' as a protocol name", str);
+ proto = protoent->p_proto;
+ }
+ if (!proto || proto == IPSET_IPPROTO_TCPUDP)
+ return syntax_err("invalid protocol '%s'", str);
+
+ return ipset_session_data_set(session, opt, &proto);
+}
+
+/**
+ * ipset_parse_proto_port - parse (optional) protocol and a single port
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a protocol and port, separated by a colon.
+ * The protocol part is optional.
+ * The parsed protocol and port numbers are stored in the data
+ * blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_proto_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ char *a, *saved, *tmp;
+ int err = 0;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PORT);
+ assert(str);
+
+ saved = tmp = strdup(str);
+ if (tmp == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+
+ a = proto_separator(tmp);
+ if (a != NULL) {
+ /* proto:port */
+ *a++ = '\0';
+ err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp);
+ if (err)
+ goto error;
+ tmp = a;
+ }
+ err = ipset_parse_single_port(session, opt, tmp);
+
+error:
+ free(saved);
+ return err;
+}
+
+/**
* ipset_parse_family - parse INET|INET6 family names
* @session: session structure
* @opt: option kind of the data
diff --git a/lib/print.c b/lib/print.c
index d96e643..68f658a 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -435,6 +435,79 @@ ipset_print_port(char *buf, unsigned int len,
return offset;
}
+/**
+ * ipset_print_proto - print protocol name
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print protocol name to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_proto(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env UNUSED)
+{
+ struct protoent *protoent;
+ uint8_t proto;
+
+ assert(buf);
+ assert(len > 0);
+ assert(data);
+ assert(opt == IPSET_OPT_PROTO);
+
+ proto = *(uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
+ assert(proto);
+
+ if (proto == IPSET_IPPROTO_ANY)
+ return snprintf(buf, len, "any");
+ protoent = getprotobynumber(proto);
+ if (protoent)
+ return snprintf(buf, len, "%s", protoent->p_name);
+
+ /* Should not happen */
+ return snprintf(buf, len, "%u", proto);
+}
+
+/**
+ * ipset_print_proto_port - print proto:port
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print protocol and port to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_proto_port(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env UNUSED)
+{
+ int size, offset = 0;
+
+ assert(buf);
+ assert(len > 0);
+ assert(data);
+ assert(opt == IPSET_OPT_PORT);
+
+ if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
+ size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env);
+ SNPRINTF_FAILURE(size, len, offset);
+ if (len < 2)
+ return -ENOSPC;
+ strcat(buf, ":");
+ SNPRINTF_FAILURE(1, len, offset);
+ }
+ return ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+}
+
#define print_second(data) \
ipset_data_flags_test(data, \
IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER))
diff --git a/lib/session.c b/lib/session.c
index 36716f0..fe1e178 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -35,6 +35,7 @@ struct ipset_session {
enum ipset_cmd cmd; /* Current command */
uint32_t lineno; /* Current lineno in restore mode */
char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */
+ const struct ipset_type *saved_type; /* Saved type */
struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */
uint8_t nestid; /* Current nest level */
bool version_checked; /* Version checked */
@@ -82,6 +83,20 @@ ipset_session_handle(const struct ipset_session *session)
return session->handle;
}
+/**
+ * ipset_saved_type - return pointer to the saved type
+ * @session: session structure
+ *
+ * Returns the pointer to the saved type from the last ipset_cmd
+ * It is required to decode type-specific error codes in restore mode.
+ */
+const struct ipset_type *
+ipset_saved_type(const struct ipset_session *session)
+{
+ assert(session);
+ return session->saved_type;
+}
+
/*
* Environment options
*/
@@ -327,6 +342,10 @@ const struct ipset_attr_policy create_attrs[] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_TIMEOUT,
},
+ [IPSET_ATTR_PROTO] = {
+ .type = MNL_TYPE_U8,
+ .opt = IPSET_OPT_PROTO,
+ },
[IPSET_ATTR_CADT_FLAGS] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_CADT_FLAGS,
@@ -394,6 +413,10 @@ const struct ipset_attr_policy adt_attrs[] = {
.type = MNL_TYPE_U16,
.opt = IPSET_OPT_PORT_TO,
},
+ [IPSET_ATTR_PROTO] = {
+ .type = MNL_TYPE_U8,
+ .opt = IPSET_OPT_PROTO,
+ },
[IPSET_ATTR_TIMEOUT] = {
.type = MNL_TYPE_U32,
.opt = IPSET_OPT_TIMEOUT,
@@ -1714,6 +1737,8 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
goto cleanup;
D("past: build_msg");
+ /* We have to save the type for error handling */
+ session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE);
/* Save setname for the next possible aggregated restore line */
if (session->lineno != 0
&& (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
diff --git a/src/errcode.c b/src/errcode.c
index c30ecb6..ae0d8c8 100644
--- a/src/errcode.c
+++ b/src/errcode.c
@@ -102,6 +102,10 @@ static const struct ipset_errcode_table hash_errcode_table[] = {
"Hash is full, cannot add more elements" },
{ IPSET_ERR_HASH_ELEM, 0,
"Null-valued element, cannot be stored in a hash type of set" },
+ { IPSET_ERR_INVALID_PROTO, 0,
+ "Invalid protocol specified" },
+ { IPSET_ERR_MISSING_PROTO, 0,
+ "Protocol missing, but must be specified" },
{ },
};
@@ -144,7 +148,7 @@ ipset_errcode(struct ipset_session *session, enum ipset_cmd cmd, int errcode)
if (errcode >= IPSET_ERR_TYPE_SPECIFIC) {
const struct ipset_type *type;
- type = ipset_session_data_get(session, IPSET_OPT_TYPE);
+ type = ipset_saved_type(session);
if (type) {
if (MATCH_TYPENAME(type->name, "bitmap:"))
table = bitmap_errcode_table;
diff --git a/src/ipset.8 b/src/ipset.8
index 661d1b4..5b9e4ad 100644
--- a/src/ipset.8
+++ b/src/ipset.8
@@ -112,12 +112,12 @@ If the set has got reference(s), nothing is done and no set destroyed.
\fBlist\fP [ \fISETNAME\fP ]
List the header data and the entries for the specified set, or for
all sets if none is given. The
-\fB\-\-resolve\fP
+\fB\-resolve\fP
option can be used to force name lookups (which may be slow). When the
-\fB\-\-sorted\fP
+\fB\-sorted\fP
option is given, the entries are listed sorted (if the given set
type supports the operation). The option
-\fB\-\-output\fR
+\fB\-output\fR
can be used to control the format of the listing:
\fBplain\fR, \fBsave\fR or \fBxml\fR.
The default is
@@ -231,7 +231,7 @@ to 65536 entries.
.PP
\fIDEL\-ENTRY\fR := { \fIipaddr\fR | \fIfromaddr\fR\-\fItoaddr\fR | \fIipaddr\fR/\fIcidr\fR }
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR }
+\fITEST\-ENTRY\fR := \fIipaddr\fR
.PP
Mandatory \fBcreate\fR options:
.TP
@@ -262,13 +262,13 @@ The \fBbitmap:ip,mac\fR set type uses a memory range to store IPv4 and a MAC add
.PP
\fICREATE\-OPTIONS\fR := \fBrange\fP \fIfrom\-ip\fP\-\fIto\-ip\fR|\fIip\fR/\fIcidr\fR [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+\fIADD\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR[,\fImac\-addr\fR] }
+\fITEST\-ENTRY\fR := \fIipaddr\fR[,\fImac\-addr\fR]
.PP
Mandatory options to use when creating a \fBbitmap:ip,mac\fR type of set:
.TP
@@ -307,7 +307,7 @@ and such a set can store up to 65536 ports.
.PP
\fIDEL\-ENTRY\fR := {\fIport\fR | \fIfrom\-port\fR\-\fIto\-port\fR }
.PP
-\fITEST\-ENTRY\fR := { \fIport\fR }
+\fITEST\-ENTRY\fR := \fIport\fR
.PP
Mandatory options to use when creating a \fBbitmap:port\fR type of set:
.TP
@@ -328,13 +328,13 @@ if that is exhausted, the doubling of the hash is performed.
.PP
\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBnetmask\fP \fIcidr\fP ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIipaddr\fR }
+\fIADD\-ENTRY\fR := \fIipaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR }
+\fITEST\-ENTRY\fR := \fIipaddr\fR
.PP
For the \fBinet\fR family one can add or delete multiple entries by specifying
a range or a network:
@@ -378,13 +378,13 @@ if that is exhausted, the doubling of the hash is performed.
.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] }
+\fIADD\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR]
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR[/\fIcidr\fR] }
+\fITEST\-ENTRY\fR := \fIipaddr\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -427,15 +427,15 @@ The \fBhash:ip,port\fR set type uses a hash to store IP address and port pairs.
In order to avoid clashes in the hash a limited number of chaining, and then
if that is exhausted, the doubling of the hash is performed.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR }
+\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR
.PP
Optional \fBcreate\fR options:
.TP
@@ -443,6 +443,12 @@ Optional \fBcreate\fR options:
The protocol family of the IP addresses to be stored in the set. The default is
\fBinet\fR, i.e IPv4.
.TP
+\fBproto\fR \fIvalue\fR
+The default protocol for the port to be stored in the set. If no protocol is specified,
+then TCP/UDP ports are assumed as backward compatibility. The default protocol
+also defines which kind of ports are to be added to the set when the \fBSET\fR
+target is used.
+.TP
\fBhashsize\fR \fIvalue\fR
The initial hash size for the set, default is 1024. The hash size must be a power
of two, the kernel automatically rounds up non power of two hash sizes to the first
@@ -451,30 +457,37 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
-The \fBhash:ip,port\fR type of sets require two \fBsrc\fR/\fBdst\fR parameters of
-the \fBset\fR match and \fBSET\fR target kernel modules.
+When adding, deleting, testing entries the port value is interpreted
+for TCP and UDP only, for other protocols the port value currently is ignored and
+zeroed out, but must be specified. The \fBhash:ip,port\fR type of sets require
+two \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR
+target kernel modules.
.PP
Examples:
.IP
-ipset create foo hash:ip,port
+ipset create foo hash:ip,port proto tcp
.IP
ipset add foo 192.168.1.1,80
.IP
+ipset add foo 192.168.1.1,udp:53
+.IP
+ipset add foo 192.168.1.1,ospf:0
+.IP
ipset test foo 192.168.1.1,80
.SS hash:ip,port,ip
The \fBhash:ip,port,ip\fR set type uses a hash to store IP address, port and
IP address triples. In order to avoid clashes in the hash a limited number of
chaining, and then if that is exhausted, the doubling of the hash is performed.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR }
+\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR
.PP
Optional \fBcreate\fR options:
.TP
@@ -482,6 +495,12 @@ Optional \fBcreate\fR options:
The protocol family of the IP addresses to be stored in the set. The default is
\fBinet\fR, i.e IPv4.
.TP
+\fBproto\fR \fIvalue\fR
+The default protocol for the port to be stored in the set. If no protocol is specified,
+then TCP/UDP ports are assumed as backward compatibility. The default protocol
+also defines which kind of ports are to be added to the set when the \fBSET\fR
+target is used.
+.TP
\fBhashsize\fR \fIvalue\fR
The initial hash size for the set, default is 1024. The hash size must be a power
of two, the kernel automatically rounds up non power of two hash sizes to the first
@@ -490,8 +509,11 @@ correct value.
\fBmaxelem\fR \fIvalue\fR
The maximal number of elements which can be stored in the set, default 65536.
.PP
-The \fBhash:ip,port,ip\fR type of sets require three \fBsrc\fR/\fBdst\fR parameters of
-the \fBset\fR match and \fBSET\fR target kernel modules.
+When adding, deleting, testing entries the port value is interpreted
+for TCP and UDP only, for other protocols the port value currently is ignored and
+zeroed out, but must be specified. The \fBhash:ip,port,ip\fR type of sets require
+three \fBsrc\fR/\fBdst\fR parameters of the \fBset\fR match and \fBSET\fR
+target kernel modules.
.PP
Examples:
.IP
@@ -499,22 +521,22 @@ ipset create foo hash:ip,port,ip
.IP
ipset add foo 192.168.1.1,80,10.0.0.1
.IP
-ipset test foo 192.168.1.1,80,10.0.0.1
+ipset test foo 192.168.1.1,udp:53,10.0.0.1
.SS hash:ip,port,net
The \fBhash:ip,port,net\fR set type uses a hash to store IP address, port and
IP network triples.
In order to avoid clashes in the hash a limited number of chaining, and then
if that is exhausted, the doubling of the hash is performed.
.PP
-\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
+\fICREATE\-OPTIONS\fR := [ \fBfamily\fR { \fBinet\fR|\fBinet6\fR } ] | [ \fBproto\fR \fIvalue\fR ] | [ \fBhashsize\fR \fIvalue\fR ] [ \fBmaxelem\fR \fIvalue\fR ] [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIADD\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+\fIADD\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR]
.PP
\fIADD\-OPTIONS\fR := [ \fBtimeout\fR \fIvalue\fR ]
.PP
-\fIDEL\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+\fIDEL\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR]
.PP
-\fITEST\-ENTRY\fR := { \fIipaddr\fR,\fIport\fR,\fIipaddr\fR[/\fIcidr\fR] }
+\fITEST\-ENTRY\fR := \fIipaddr\fR,[\fIproto\fR:]\fIport\fR,\fIipaddr\fR[/\fIcidr\fR]
.PP
Optional \fBcreate\fR options:
.TP
@@ -522,6 +544,12 @@ Optional \fBcreate\fR options:
The protocol family of the IP addresses to be stored in the set. The default is
\fBinet\fR, i.e IPv4.
.TP
+\fBproto\fR \fIvalue\fR
+The default protocol for the port to be stored in the set. If no protocol is specified,
+then TCP/UDP ports are assumed as backward compatibility. The default protocol
+also defines which kind of ports are to be added to the set when the \fBSET\fR
+target is used.
+.TP
\fBhashsize\fR \fIvalue\fR
The initial hash size for the set, default is 1024. The hash size must be a power
of two, the kernel automatically rounds up non power of two hash sizes to the first
@@ -531,7 +559,9 @@ correct value.
The maximal number of elements which can be stored in the set, default 65536.
.PP
When adding/deleting/testing entries, if the cidr parameter is not specified,
-then the host cidr value is assumed.
+then the host cidr value is assumed. The port value is interpreted
+for TCP and UDP only, for other protocols the port value currently is ignored and
+zeroed out, but must be specified.
.PP
From the \fBset\fR netfilter match point of view a triple will be in a \fBhash:ip,port,net\fR type of set (when the first IP and the port match)
if the second IP belongs to any of the netblocks added to the set.
diff --git a/src/ipset.c b/src/ipset.c
index 69fcd09..c613b24 100644
--- a/src/ipset.c
+++ b/src/ipset.c
@@ -202,7 +202,7 @@ restore(char *argv0)
}
static int
-call_parser(int argc, char *argv[], const struct ipset_arg *args)
+call_parser(int *argc, char *argv[], const struct ipset_arg *args)
{
int i = 1, ret = 0;
const struct ipset_arg *arg;
@@ -212,8 +212,8 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args)
if (!args)
goto done;
for (arg = args; arg->opt; arg++) {
- for (i = 1; i < argc; ) {
- D("argc: %u, i: %u: %s vs %s", argc, i, argv[i], arg->name[0]);
+ for (i = 1; i < *argc; ) {
+ D("argc: %u, i: %u: %s vs %s", *argc, i, argv[i], arg->name[0]);
if (!(ipset_match_option(argv[i], arg->name))) {
i++;
continue;
@@ -221,24 +221,24 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args)
optstr = argv[i];
/* Shift off matched option */
D("match %s", arg->name[0]);
- ipset_shift_argv(&argc, argv, i);
- D("argc: %u, i: %u", argc, i);
+ ipset_shift_argv(argc, argv, i);
+ D("argc: %u, i: %u", *argc, i);
switch (arg->has_arg) {
case IPSET_MANDATORY_ARG:
- if (i + 1 > argc)
+ if (i + 1 > *argc)
return exit_error(PARAMETER_PROBLEM,
"Missing mandatory argument of option `%s'",
arg->name[0]);
/* Fall through */
case IPSET_OPTIONAL_ARG:
- if (i + 1 <= argc) {
+ if (i + 1 <= *argc) {
ret = ipset_call_parser(session,
arg->parse,
optstr, arg->opt,
argv[i]);
if (ret < 0)
return ret;
- ipset_shift_argv(&argc, argv, i);
+ ipset_shift_argv(argc, argv, i);
break;
}
/* Fall through */
@@ -253,7 +253,7 @@ call_parser(int argc, char *argv[], const struct ipset_arg *args)
}
}
done:
- if (i < argc)
+ if (i < *argc)
return exit_error(PARAMETER_PROBLEM,
"Unknown argument: `%s'",
argv[i]);
@@ -458,6 +458,9 @@ parse_commandline(int argc, char *argv[])
}
return exit_error(NO_PROBLEM, NULL);
}
+ if (argc > 1)
+ return exit_error(PARAMETER_PROBLEM,
+ "No command specified: unknown argument %s", argv[1]);
return exit_error(PARAMETER_PROBLEM, "No command specified.");
case IPSET_CMD_VERSION:
printf("%s v%s.\n", program_name, program_version);
@@ -522,7 +525,7 @@ parse_commandline(int argc, char *argv[])
return handle_error();
/* Parse create options */
- ret = call_parser(argc, argv, type->args[IPSET_CREATE]);
+ ret = call_parser(&argc, argv, type->args[IPSET_CREATE]);
if (ret < 0)
return handle_error();
else if (ret)
@@ -557,6 +560,9 @@ parse_commandline(int argc, char *argv[])
case IPSET_CMD_RESTORE:
/* Restore mode */
+ if (argc > 1)
+ return exit_error(PARAMETER_PROBLEM,
+ "Unknown argument %s", argv[1]);
return restore(argv[0]);
case IPSET_CMD_ADD:
case IPSET_CMD_DEL:
@@ -576,7 +582,7 @@ parse_commandline(int argc, char *argv[])
return handle_error();
/* Parse additional ADT options */
- ret = call_parser(argc, argv, type->args[cmd2cmd(cmd)]);
+ ret = call_parser(&argc, argv, type->args[cmd2cmd(cmd)]);
if (ret < 0)
return handle_error();
else if (ret)
@@ -590,6 +596,9 @@ parse_commandline(int argc, char *argv[])
break;
}
+ if (argc > 1)
+ return exit_error(PARAMETER_PROBLEM,
+ "Unknown argument %s", argv[1]);
ret = ipset_cmd(session, cmd, restore_line);
D("ret %d", ret);
/* Special case for TEST and non-quiet mode */
diff --git a/src/ipset_hash_ipport.c b/src/ipset_hash_ipport.c
index 4a9b8cf..94a8cc6 100644
--- a/src/ipset_hash_ipport.c
+++ b/src/ipset_hash_ipport.c
@@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipport_create_args[] = {
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
+ { .name = { "proto", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
+ .parse = ipset_parse_proto, .print = ipset_print_proto,
+ },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
@@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipport_add_args[] = {
static const char hash_ipport_usage[] =
"create SETNAME hash:ip,port\n"
-" [family inet|inet6]\n"
+" [family inet|inet6] [proto PROTO]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
-"add SETNAME IP,PORT [timeout VALUE]\n"
-"del SETNAME IP,PORT\n"
-"test SETNAME IP,PORT\n";
+"add SETNAME IP,[PROTO:]PORT [timeout VALUE]\n"
+"del SETNAME IP,[PROTO:]PORT\n"
+"test SETNAME IP,[PROTO:]PORT\n";
struct ipset_type ipset_hash_ipport0 = {
.name = "hash:ip,port",
@@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipport0 = {
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
- .parse = ipset_parse_single_port,
- .print = ipset_print_port,
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
},
@@ -112,14 +116,18 @@ struct ipset_type ipset_hash_ipport0 = {
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_PORT),
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO),
[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
- | IPSET_FLAG(IPSET_OPT_PORT),
+ | IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO),
},
.usage = hash_ipport_usage,
diff --git a/src/ipset_hash_ipportip.c b/src/ipset_hash_ipportip.c
index 299e362..dc121e4 100644
--- a/src/ipset_hash_ipportip.c
+++ b/src/ipset_hash_ipportip.c
@@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipportip_create_args[] = {
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
+ { .name = { "proto", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
+ .parse = ipset_parse_proto, .print = ipset_print_proto,
+ },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
@@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipportip_add_args[] = {
static const char hash_ipportip_usage[] =
"create SETNAME hash:ip,port,ip\n"
-" [family inet|inet6]\n"
+" [family inet|inet6] [proto PROTO]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
-"add SETNAME IP,PORT,IP [timeout VALUE]\n"
-"del SETNAME IP,PORT,IP\n"
-"test SETNAME IP,PORT,IP\n";
+"add SETNAME IP,[PROTO:]PORT,IP [timeout VALUE]\n"
+"del SETNAME IP,[PROTO:]PORT,IP\n"
+"test SETNAME IP,[PROTO:]PORT,IP\n";
struct ipset_type ipset_hash_ipportip0 = {
.name = "hash:ip,port,ip",
@@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipportip0 = {
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
- .parse = ipset_parse_single_port,
- .print = ipset_print_port,
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
[IPSET_DIM_THREE] = {
@@ -120,16 +124,20 @@ struct ipset_type ipset_hash_ipportip0 = {
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
| IPSET_FLAG(IPSET_OPT_PORT)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_IP2)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[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),
},
diff --git a/src/ipset_hash_ipportnet.c b/src/ipset_hash_ipportnet.c
index 13a0487..a668c5e 100644
--- a/src/ipset_hash_ipportnet.c
+++ b/src/ipset_hash_ipportnet.c
@@ -37,6 +37,10 @@ static const struct ipset_arg hash_ipportnet_create_args[] = {
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_TIMEOUT,
.parse = ipset_parse_uint32, .print = ipset_print_number,
},
+ { .name = { "proto", NULL },
+ .has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROTO,
+ .parse = ipset_parse_proto, .print = ipset_print_proto,
+ },
/* Backward compatibility */
{ .name = { "probes", NULL },
.has_arg = IPSET_MANDATORY_ARG, .opt = IPSET_OPT_PROBES,
@@ -71,12 +75,12 @@ static const struct ipset_arg hash_ipportnet_add_args[] = {
static const char hash_ipportnet_usage[] =
"create SETNAME hash:ip,port,net\n"
-" [family inet|inet6]\n"
+" [family inet|inet6] [proto PROTO]\n"
" [hashsize VALUE] [maxelem VALUE]\n"
" [timeout VALUE]\n"
-"add SETNAME IP,PORT,IP[/CIDR] [timeout VALUE]\n"
-"del SETNAME IP,PORT,IP[/CIDR]\n"
-"test SETNAME IP,PORT,IP[/CIDR]\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";
struct ipset_type ipset_hash_ipportnet0 = {
.name = "hash:ip,port,net",
@@ -91,8 +95,8 @@ struct ipset_type ipset_hash_ipportnet0 = {
.opt = IPSET_OPT_IP
},
[IPSET_DIM_TWO] = {
- .parse = ipset_parse_single_port,
- .print = ipset_print_port,
+ .parse = ipset_parse_proto_port,
+ .print = ipset_print_proto_port,
.opt = IPSET_OPT_PORT
},
[IPSET_DIM_THREE] = {
@@ -120,18 +124,22 @@ struct ipset_type ipset_hash_ipportnet0 = {
.full = {
[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
| IPSET_FLAG(IPSET_OPT_MAXELEM)
+ | IPSET_FLAG(IPSET_OPT_PROTO)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_ADD] = 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)
| IPSET_FLAG(IPSET_OPT_TIMEOUT),
[IPSET_DEL] = 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),
[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),
},
diff --git a/tests/hash:ip,port,ip.t b/tests/hash:ip,port,ip.t
index 3c74cd9..cc6783b 100644
--- a/tests/hash:ip,port,ip.t
+++ b/tests/hash:ip,port,ip.t
@@ -1,51 +1,51 @@
-# Range: Create a set with timeout
+# Create a set with timeout
0 ipset create test hash:ip,port,ip timeout 5
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset add test 2.0.0.1,0,0.0.0.0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset test test 2.0.0.1,0,0.0.0.0
-# Range: Delete party zero valued element
+# Delete party zero valued element
0 ipset del test 2.0.0.1,0,0.0.0.0
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset add test 2.0.0.1,0,0.0.0.1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset test test 2.0.0.1,0,0.0.0.1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset del test 2.0.0.1,0,0.0.0.1
-# Range: Add lower boundary
+# Add lower boundary
0 ipset add test 2.0.0.1,5,1.1.1.1
-# Range: Add upper boundary
+# Add upper boundary
0 ipset add test 2.1.0.0,128,2.2.2.2
-# Range: Test lower boundary
+# Test lower boundary
0 ipset test test 2.0.0.1,5,1.1.1.1
-# Range: Test upper boundary
+# Test upper boundary
0 ipset test test 2.1.0.0,128,2.2.2.2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2.0.0.1,5,1.1.1.2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2.0.0.1,6,1.1.1.1
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2.0.0.2,6,1.1.1.1
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset test test 2.0.0.0,5,1.1.1.1
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset test test 2.1.0.1,128,2.2.2.2
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset add test 2.0.0.0,5,1.1.1.1
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset add test 2.1.0.1,128,2.2.2.2
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list0 && rm .foo
-# Range: Sleep 6s so that elements can time out
+# Sleep 6s so that elements can time out
0 sleep 6
-# Range: List set
+# List set
0 ipset list test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip,port,ip.t.list1 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset flush test
-# Range: Delete test set
+# Delete test set
0 ipset destroy test
# eof
diff --git a/tests/hash:ip,port.t b/tests/hash:ip,port.t
index cb88f96..e3b292b 100644
--- a/tests/hash:ip,port.t
+++ b/tests/hash:ip,port.t
@@ -1,43 +1,65 @@
-# Range: Create a set with timeout
+# Create a set with timeout
0 ipset create test hash:ip,port timeout 5
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset add test 2.0.0.1,0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset test test 2.0.0.1,0
-# Range: Delete partly zero valued element
+# Delete partly zero valued element
0 ipset del test 2.0.0.1,0
-# Range: Add lower boundary
+# Add lower boundary
0 ipset add test 2.0.0.1,5
-# Range: Add upper boundary
+# Add upper boundary
0 ipset add test 2.1.0.0,128
-# Range: Test lower boundary
+# Test lower boundary
0 ipset test test 2.0.0.1,5
-# Range: Test upper boundary
+# Test upper boundary
0 ipset test test 2.1.0.0,128
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2.0.0.1,4
-# Range: Delete value not added to the set
+# Delete value not added to the set
1 ipset del test 2.0.0.1,6
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset test test 2.0.0.0,5
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset test test 2.1.0.1,128
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset add test 2.0.0.0,5
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset add test 2.1.0.1,128
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list0 && rm .foo
-# Range: Sleep 6s so that elements can time out
+# Sleep 6s so that elements can time out
0 sleep 6
-# Range: List set
+# List set
0 ipset list test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list1 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset flush test
-# Range: Delete test set
+# Delete test set
+0 ipset destroy test
+# Create a set with default TCP protocol
+0 ipset create test hash:ip,port proto tcp
+# Add element without specifying protocol
+0 ipset add test 2.0.0.1,80
+# Add "same" element but with UDP protocol
+0 ipset add test 2.0.0.1,udp:80
+# Test element without specifying protocol
+0 ipset test test 2.0.0.1,80
+# Test element with TCP protocol
+0 ipset test test 2.0.0.1,tcp:80
+# Test element with UDP protocol
+0 ipset test test 2.0.0.1,udp:80
+# Add element with ospf
+0 ipset add test 2.0.0.1,ospf:0
+# Test element with ospf
+0 ipset test test 2.0.0.1,ospf:0
+# List set
+0 ipset list test > .foo0 && ./sort.sh .foo0
+# Check listing
+0 diff -I 'Size in memory.*' .foo hash:ip,port.t.list2 && rm .foo
+# Delete set
0 ipset destroy test
# eof
diff --git a/tests/hash:ip,port.t.list2 b/tests/hash:ip,port.t.list2
new file mode 100644
index 0000000..e8a9db0
--- /dev/null
+++ b/tests/hash:ip,port.t.list2
@@ -0,0 +1,10 @@
+Name: test
+Type: hash:ip,port
+Header: family inet hashsize 1024 maxelem 65536 proto tcp
+Size in memory: 8424
+References: 0
+Members:
+2.0.0.1,ospf:0
+2.0.0.1,tcp:80
+2.0.0.1,udp:80
+
diff --git a/tests/hash:ip6,port,ip6.t b/tests/hash:ip6,port,ip6.t
index 84afdf9..712db33 100644
--- a/tests/hash:ip6,port,ip6.t
+++ b/tests/hash:ip6,port,ip6.t
@@ -1,51 +1,51 @@
-# Range: Create a set with timeout
+# Create a set with timeout
0 ipset create test hash:ip,port,ip family inet6 timeout 5
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset add test 2:0:0::1,0,0:0:0::0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset test test 2:0:0::1,0,0:0:0::0
-# Range: Delete party zero valued element
+# Delete party zero valued element
0 ipset del test 2:0:0::1,0,0:0:0::0
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset add test 2:0:0::1,0,0:0:0::1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset test test 2:0:0::1,0,0:0:0::1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset del test 2:0:0::1,0,0:0:0::1
-# Range: Add lower boundary
+# Add lower boundary
0 ipset add test 2:0:0::1,5,1:1:1::1
-# Range: Add upper boundary
+# Add upper boundary
0 ipset add test 2:1:0::0,128,2:2:2::2
-# Range: Test lower boundary
+# Test lower boundary
0 ipset test test 2:0:0::1,5,1:1:1::1
-# Range: Test upper boundary
+# Test upper boundary
0 ipset test test 2:1:0::0,128,2:2:2::2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2:0:0::1,5,1:1:1::2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2:0:0::1,6,1:1:1::1
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2:0:0::2,6,1:1:1::1
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset test test 2:0:0::0,5,1:1:1::1
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset test test 2:1:0::1,128,2:2:2::2
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset add test 2:0:0::0,5,1:1:1::1
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset add test 2:1:0::1,128,2:2:2::2
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list0 && rm .foo
-# Range: Sleep 6s so that elements can time out
+# Sleep 6s so that elements can time out
0 sleep 6
-# Range: List set
+# List set
0 ipset list test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip6,port,ip6.t.list1 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset flush test
-# Range: Delete test set
+# Delete test set
0 ipset destroy test
# eof
diff --git a/tests/hash:ip6,port.t b/tests/hash:ip6,port.t
index feb525c..5b00942 100644
--- a/tests/hash:ip6,port.t
+++ b/tests/hash:ip6,port.t
@@ -1,43 +1,43 @@
-# Range: Create a set with timeout
+# Create a set with timeout
0 ipset create test hash:ip,port family inet6 timeout 5
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset add test 2:0:0::1,0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset test test 2:0:0::1,0
-# Range: Delete partly zero valued element
+# Delete partly zero valued element
0 ipset del test 2:0:0::1,0
-# Range: Add lower boundary
+# Add lower boundary
0 ipset add test 2:0:0::1,5
-# Range: Add upper boundary
+# Add upper boundary
0 ipset add test 2:1:0::0,128
-# Range: Test lower boundary
+# Test lower boundary
0 ipset test test 2:0:0::1,5
-# Range: Test upper boundary
+# Test upper boundary
0 ipset test test 2:1:0::0,128
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2:0:0::1,4
-# Range: Delete value not added to the set
+# Delete value not added to the set
1 ipset del test 2:0:0::1,6
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset test test 2:0:0::0,5
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset test test 2:1:0::1,128
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset add test 2:0:0::0,5
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset add test 2:1:0::1,128
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list0 && rm .foo
-# Range: Sleep 6s so that elements can time out
+# Sleep 6s so that elements can time out
0 sleep 6
-# Range: List set
+# List set
0 ipset list test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:ip6,port.t.list1 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset flush test
-# Range: Delete test set
+# Delete test set
0 ipset destroy test
# eof
diff --git a/tests/hash:net.t b/tests/hash:net.t
index b46ac86..c27bb65 100644
--- a/tests/hash:net.t
+++ b/tests/hash:net.t
@@ -1,40 +1,40 @@
# Create a set with timeout
0 ipset create test nethash hashsize 128 timeout 6
-# Range: Add zero valued element
+# Add zero valued element
1 ipset add test 0.0.0.0/0
-# Range: Test zero valued element
+# Test zero valued element
1 ipset test test 0.0.0.0/0
-# Range: Delete zero valued element
+# Delete zero valued element
1 ipset del test 0.0.0.0/0
-# Range: Try to add /0
+# Try to add /0
1 ipset add test 1.1.1.1/0
-# Range: Try to add /32
+# Try to add /32
0 ipset add test 1.1.1.1/32
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset add test 0.0.0.0/1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset test test 0.0.0.0/1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset del test 0.0.0.0/1
-# Range: Test deleted element
+# Test deleted element
1 ipset test test 0.0.0.0/1
-# Range: Delete element not added to the set
+# Delete element not added to the set
1 ipset del test 0.0.0.0/1
-# Range: Add first random network
+# Add first random network
0 ipset add test 2.0.0.1/24
-# Range: Add second random network
+# Add second random network
0 ipset add test 192.168.68.69/27
-# Range: Test first random value
+# Test first random value
0 ipset test test 2.0.0.255
-# Range: Test second random value
+# Test second random value
0 ipset test test 192.168.68.95
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 2.0.1.0
-# Range: Try to add IP address
+# Try to add IP address
0 ipset add test 2.0.0.1
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:net.t.list0 && rm .foo
# Sleep 6s so that element can time out
0 sleep 6
diff --git a/tests/hash:net6.t b/tests/hash:net6.t
index e4246d9..5a575d1 100644
--- a/tests/hash:net6.t
+++ b/tests/hash:net6.t
@@ -1,40 +1,40 @@
# Create a set with timeout
0 ipset create test nethash family inet6 hashsize 128 timeout 6
-# Range: Add zero valued element
+# Add zero valued element
1 ipset add test ::/0
-# Range: Test zero valued element
+# Test zero valued element
1 ipset test test ::/0
-# Range: Delete zero valued element
+# Delete zero valued element
1 ipset del test ::/0
-# Range: Try to add /0
+# Try to add /0
1 ipset add test 1:1:1::1/0
-# Range: Try to add /32
+# Try to add /32
0 ipset add test 1:1:1::1/128
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset add test 0:0:0::0/1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset test test 0:0:0::0/1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset del test 0:0:0::0/1
-# Range: Test deleted element
+# Test deleted element
1 ipset test test 0:0:0::0/1
-# Range: Delete element not added to the set
+# Delete element not added to the set
1 ipset del test 0:0:0::0/1
-# Range: Add first random network
+# Add first random network
0 ipset add test 2:0:0::1/24
-# Range: Add second random network
+# Add second random network
0 ipset add test 192:168:68::69/27
-# Range: Test first random value
+# Test first random value
0 ipset test test 2:0:0::255
-# Range: Test second random value
+# Test second random value
0 ipset test test 192:168:68::95
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset test test 3:0:0::1
-# Range: Try to add IP address
+# Try to add IP address
0 ipset add test 3:0:0::1
-# Range: List set
+# List set
0 ipset list test | sed 's/timeout ./timeout x/' > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo hash:net6.t.list0 && rm .foo
# Sleep 6s so that element can time out
0 sleep 6
diff --git a/tests/init.t b/tests/init.t
index ac15311..3311856 100644
--- a/tests/init.t
+++ b/tests/init.t
@@ -4,4 +4,6 @@
1 ipset -L test >/dev/null
# Delete our test set: the testsuite fails if it exists
1 ipset -X test
+# Check mandatory create arguments
+2 ipset -N test
# eof
diff --git a/tests/ipmap.t b/tests/ipmap.t
index 00c35e8..2373176 100644
--- a/tests/ipmap.t
+++ b/tests/ipmap.t
@@ -1,3 +1,11 @@
+# Range: Check syntax error: missing range/from-to
+2 ipset -N test ipmap
+# Range: Check syntax error: missing --from
+2 ipset -N test ipmap --to 2.1.0.1
+# Range: Check syntax error: missing --to
+2 ipset -N test ipmap --from 2.1.0.1
+# Range: Catch invalid IPv4 address
+1 ipset -N test ipmap --from 2.0.0.256 --to 2.1.0.1
# Range: Try to create from an invalid range
1 ipset -N test ipmap --from 2.0.0.1 --to 2.1.0.1
# Range: Create a set from a valid range
@@ -28,6 +36,16 @@
0 ipset -D test 2.0.0.128
# Range: Add a range of elements
0 ipset -A test 2.0.0.128-2.0.0.131
+# Range: Save set
+0 ipset -S test > ipmap.t.restore
+# Range: Destroy set
+0 ipset -X test
+# Range: Restore set and catch error
+1 sed 's/2.0.0.131/222.0.0.131/' < ipmap.t.restore | ipset -R
+# Range: Destroy set
+0 ipset -X test
+# Range: Restore set
+0 ipset -R < ipmap.t.restore && rm ipmap.t.restore
# Range: List set
0 ipset -L test > .foo
# Range: Check listing
diff --git a/tests/ipporthash.t b/tests/ipporthash.t
index dc35bcd..9847830 100644
--- a/tests/ipporthash.t
+++ b/tests/ipporthash.t
@@ -1,71 +1,71 @@
-# Range: Create a set from a range (range ignored)
+# Create a set from a range (range ignored)
0 ipset -N test ipporthash --from 2.0.0.1 --to 2.1.0.0
-# Range: Destroy set
+# Destroy set
0 ipset -X test
-# Range: Create a set
+# Create a set
0 ipset -N test ipporthash
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset -A test 2.0.0.1,0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset -T test 2.0.0.1,0
-# Range: Delete partly zero valued element
+# Delete partly zero valued element
0 ipset -D test 2.0.0.1,0
-# Range: Add lower boundary
+# Add lower boundary
0 ipset -A test 2.0.0.1,5
-# Range: Add upper boundary
+# Add upper boundary
0 ipset -A test 2.1.0.0,128
-# Range: Test lower boundary
+# Test lower boundary
0 ipset -T test 2.0.0.1,5
-# Range: Test upper boundary
+# Test upper boundary
0 ipset -T test 2.1.0.0,128
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.1,4
-# Range: Delete value not added to the set
+# Delete value not added to the set
1 ipset -D test 2.0.0.1,6
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset -T test 2.0.0.0,5
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset -T test 2.1.0.1,128
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset -A test 2.0.0.0,5
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset -A test 2.1.0.1,128
-# Range: List set
+# List set
0 ipset -L test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo ipporthash.t.list0 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset -F test
-# Range: Delete test set
+# Delete test set
0 ipset -X test
-# Network: Create a set from a network (network ignored)
+# Create a set from a network (network ignored)
0 ipset -N test ipporthash --network 2.0.0.0/16
-# Network: Add lower boundary
+# Add lower boundary
0 ipset -A test 2.0.0.0,5
-# Network: Add upper boundary
+# Add upper boundary
0 ipset -A test 2.0.255.255,128
-# Network: Test lower boundary
+# Test lower boundary
0 ipset -T test 2.0.0.0,5
-# Network: Test upper boundary
+# Test upper boundary
0 ipset -T test 2.0.255.255,128
-# Network: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.0,4
-# Network: Delete value not added to the set
+# Delete value not added to the set
1 ipset -D test 2.0.0.0,6
-# Network: Test value before lower boundary
+# Test value before lower boundary
1 ipset -T test 1.255.255.255,5
-# Network: Test value after upper boundary
+# Test value after upper boundary
1 ipset -T test 2.1.0.0,128
-# Network: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset -A test 1.255.255.255,5
-# Network: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset -A test 2.1.0.0,128
-# Network: List set
+# List set
0 ipset -L test > .foo0 && ./sort.sh .foo0
-# Network: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo ipporthash.t.list1 && rm .foo
-# Network: Flush test set
+# Flush test set
0 ipset -F test
-# Network: Delete test set
+# Delete test set
0 ipset -X test
# eof
diff --git a/tests/ipportiphash.t b/tests/ipportiphash.t
index 3f61947..f6e79cc 100644
--- a/tests/ipportiphash.t
+++ b/tests/ipportiphash.t
@@ -1,81 +1,81 @@
-# Range: Create a set from a range (range ignored)
+# Create a set from a range (range ignored)
0 ipset -N test ipportiphash --from 2.0.0.1 --to 2.1.0.0
-# Range: Destroy set
+# Destroy set
0 ipset -X test
-# Range: Create a set
+# Create a set
0 ipset -N test ipportiphash
-# Range: Add partly zero valued element
+# Add partly zero valued element
0 ipset -A test 2.0.0.1,0,0.0.0.0
-# Range: Test partly zero valued element
+# Test partly zero valued element
0 ipset -T test 2.0.0.1,0,0.0.0.0
-# Range: Delete party zero valued element
+# Delete party zero valued element
0 ipset -D test 2.0.0.1,0,0.0.0.0
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset -A test 2.0.0.1,0,0.0.0.1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset -T test 2.0.0.1,0,0.0.0.1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset -D test 2.0.0.1,0,0.0.0.1
-# Range: Add lower boundary
+# Add lower boundary
0 ipset -A test 2.0.0.1,5,1.1.1.1
-# Range: Add upper boundary
+# Add upper boundary
0 ipset -A test 2.1.0.0,128,2.2.2.2
-# Range: Test lower boundary
+# Test lower boundary
0 ipset -T test 2.0.0.1,5,1.1.1.1
-# Range: Test upper boundary
+# Test upper boundary
0 ipset -T test 2.1.0.0,128,2.2.2.2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.1,5,1.1.1.2
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.1,6,1.1.1.1
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.2,6,1.1.1.1
-# Range: Test value before lower boundary
+# Test value before lower boundary
1 ipset -T test 2.0.0.0,5,1.1.1.1
-# Range: Test value after upper boundary
+# Test value after upper boundary
1 ipset -T test 2.1.0.1,128,2.2.2.2
-# Range: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset -A test 2.0.0.0,5,1.1.1.1
-# Range: Try to add value after upper boundary
+# Try to add value after upper boundary
0 ipset -A test 2.1.0.1,128,2.2.2.2
-# Range: List set
+# List set
0 ipset -L test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo ipportiphash.t.list0 && rm .foo
-# Range: Flush test set
+# Flush test set
0 ipset -F test
-# Range: Delete test set
+# Delete test set
0 ipset -X test
-# Network: Create a set from a valid network (network ignored)
+# Create a set from a valid network (network ignored)
0 ipset -N test ipportiphash --network 2.0.0.0/16
-# Network: Add lower boundary
+# Add lower boundary
0 ipset -A test 2.0.0.0,5,1.1.1.1
-# Network: Add upper boundary
+# Add upper boundary
0 ipset -A test 2.0.255.255,128,2.2.2.2
-# Network: Test lower boundary
+# Test lower boundary
0 ipset -T test 2.0.0.0,5,1.1.1.1
-# Network: Test upper boundary
+# Test upper boundary
0 ipset -T test 2.0.255.255,128,2.2.2.2
-# Network: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.0,5,1.1.1.2
-# Network: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.0.0,6,1.1.1.1
-# Network: Test value before lower boundary
+# Test value before lower boundary
1 ipset -T test 1.255.255.255,5,1.1.1.1
-# Network: Test value after upper boundary
+# Test value after upper boundary
1 ipset -T test 2.1.0.0,128,2.2.2.2
-# Network: Try to add value before lower boundary
+# Try to add value before lower boundary
0 ipset -A test 1.255.255.255,5,1.1.1.1
-# Network: Try to test value before lower boundary
+# Try to test value before lower boundary
0 ipset -T test 1.255.255.255,5,1.1.1.1
-# Network: Try to del value before lower boundary
+# Try to del value before lower boundary
0 ipset -D test 1.255.255.255,5,1.1.1.1
-# Network: List set
+# List set
0 ipset -L test > .foo0 && ./sort.sh .foo0
-# Network: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo ipportiphash.t.list1 && rm .foo
-# Network: Flush test set
+# Flush test set
0 ipset -F test
-# Network: Delete test set
+# Delete test set
0 ipset -X test
# eof
diff --git a/tests/nethash.t b/tests/nethash.t
index 400e110..537c0db 100644
--- a/tests/nethash.t
+++ b/tests/nethash.t
@@ -1,40 +1,40 @@
# Create a set
0 ipset -N test nethash --hashsize 128
-# Range: Add zero valued element
+# Add zero valued element
1 ipset -A test 0.0.0.0/0
-# Range: Test zero valued element
+# Test zero valued element
1 ipset -T test 0.0.0.0/0
-# Range: Delete zero valued element
+# Delete zero valued element
1 ipset -D test 0.0.0.0/0
-# Range: Try to add /0
+# Try to add /0
1 ipset -A test 1.1.1.1/0
-# Range: Try to add /32
+# Try to add /32
0 ipset -A test 1.1.1.1/32
-# Range: Add almost zero valued element
+# Add almost zero valued element
0 ipset -A test 0.0.0.0/1
-# Range: Test almost zero valued element
+# Test almost zero valued element
0 ipset -T test 0.0.0.0/1
-# Range: Delete almost zero valued element
+# Delete almost zero valued element
0 ipset -D test 0.0.0.0/1
-# Range: Test deleted element
+# Test deleted element
1 ipset -T test 0.0.0.0/1
-# Range: Delete element not added to the set
+# Delete element not added to the set
1 ipset -D test 0.0.0.0/1
-# Range: Add first random network
+# Add first random network
0 ipset -A test 2.0.0.1/24
-# Range: Add second random network
+# Add second random network
0 ipset -A test 192.168.68.69/27
-# Range: Test first random value
+# Test first random value
0 ipset -T test 2.0.0.255
-# Range: Test second random value
+# Test second random value
0 ipset -T test 192.168.68.95
-# Range: Test value not added to the set
+# Test value not added to the set
1 ipset -T test 2.0.1.0
-# Range: Try to add IP address
+# Try to add IP address
0 ipset -A test 2.0.0.1
-# Range: List set
+# List set
0 ipset -L test > .foo0 && ./sort.sh .foo0
-# Range: Check listing
+# Check listing
0 diff -I 'Size in memory.*' .foo nethash.t.list0 && rm .foo
# Flush test set
0 ipset -F test
diff --git a/tests/setlist.t b/tests/setlist.t
index f80f5ae..d673e8e 100644
--- a/tests/setlist.t
+++ b/tests/setlist.t
@@ -1,55 +1,55 @@
-# Setlist: Create dummy set
+# Create dummy set
0 ipset -N dummy list:set
-# Setlist: Create base set foo
+# Create base set foo
0 ipset -N foo ipmap --from 2.0.0.1 --to 2.1.0.0
-# Setlist: Create base set bar
+# Create base set bar
0 ipset -N bar iphash
-# Setlist: Create setlist kind of set
+# Create setlist kind of set
0 ipset -N test setlist
-# Setlist: Swap test and dumy sets
+# Swap test and dumy sets
0 ipset -W test dummy
-# Setlist: Destroy dummy set
+# Destroy dummy set
0 ipset -X dummy
-# Setlist: Add foo set to setlist
+# Add foo set to setlist
0 ipset -A test foo
-# Setlist: Test foo set in setlist
+# Test foo set in setlist
0 ipset -T test foo
-# Setlist: Test nonexistent set in setlist
+# Test nonexistent set in setlist
1 ipset -T test nonexistent
-# Setlist: Try to delete foo set
+# Try to delete foo set
1 ipset -X foo
-# Setlist: Add bar set to setlist, after foo
+# Add bar set to setlist, after foo
0 ipset -A test bar
-# Setlist: Test bar,after,foo
+# Test bar,after,foo
0 ipset -T test bar,after,foo
-# Setlist: Test foo,before,bar
+# Test foo,before,bar
0 ipset -T test foo,before,bar
-# Setlist: Test bar,before,foo
+# Test bar,before,foo
1 ipset -T test bar,before,foo
-# Setlist: Test foo,after,bar
+# Test foo,after,bar
1 ipset -T test foo,after,bar
-# Setlist: Save sets
+# Save sets
0 ipset -S > setlist.t.restore
-# Setlist: Delete bar,before,foo
+# Delete bar,before,foo
1 ipset -D test bar,before,foo
-# Setlist: Delete foo,after,bar
+# Delete foo,after,bar
1 ipset -D test foo,after,bar
-# Setlist: Delete bar,after,foo
+# Delete bar,after,foo
0 ipset -D test bar,after,foo
-# Setlist: Flush test set
+# Flush test set
0 ipset -F test
-# Setlist: Delete test set
+# Delete test set
0 ipset -X test
-# Setlist: Delete all sets
+# Delete all sets
0 ipset -X
-# Setlist: Restore saved sets
+# Restore saved sets
0 ipset -R < setlist.t.restore
-# Setlist: List set
+# List set
0 ipset -L test > .foo
-# Setlist: Check listing
+# Check listing
0 diff .foo setlist.t.list0 && rm .foo
-# Setlist: Flush all sets
+# Flush all sets
0 ipset -F
-# Setlist: Delete all sets
+# Delete all sets
0 ipset -X && rm setlist.t.restore
# eof