All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ipset: forceadd support
@ 2014-02-01 13:30 Josh Hunt
  2014-02-01 13:30 ` [PATCH 1/2] ipset: add forceadd kernel support for hash set types Josh Hunt
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Josh Hunt @ 2014-02-01 13:30 UTC (permalink / raw)
  To: netfilter-devel, kadlec; +Cc: Josh Hunt

Forceadd is a property for hash set types and is passed in duration creation.
When sets with this property enabled are full each subsequent 'add' operation
will attempt to evict a random entry from the set. The implementation tries to
keep the overhead low by checking to see which bucket the new entry hashes to.
If that bucket has any entries it will evict the first one and add the new
entry.

Ex usage:
ipset create foo hash:ip forceadd

The intended usecase is for things like 'ban' lists where you may not be concerned
with possibly evicting something early, and more concerned with the size of
the hash itself.


Josh Hunt (2):
  ipset: add forceadd kernel support for hash set types
  ipset: add forceadd userspace support for hash set types

 include/libipset/data.h                            |    4 +-
 include/libipset/linux_ip_set.h                    |    1 +
 include/libipset/parse.h                           |    3 +
 kernel/include/uapi/linux/netfilter/ipset/ip_set.h |    1 +
 kernel/net/netfilter/ipset/ip_set_hash_gen.h       |   26 +++-
 kernel/net/netfilter/ipset/ip_set_hash_ip.c        |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_ipmark.c    |    3 +-
 kernel/net/netfilter/ipset/ip_set_hash_ipport.c    |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_ipportip.c  |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_net.c       |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_netiface.c  |    4 +-
 kernel/net/netfilter/ipset/ip_set_hash_netnet.c    |    3 +-
 kernel/net/netfilter/ipset/ip_set_hash_netport.c   |    4 +-
 .../net/netfilter/ipset/ip_set_hash_netportnet.c   |    4 +-
 lib/data.c                                         |    8 +
 lib/debug.c                                        |    1 +
 lib/ipset_hash_ip.c                                |  123 ++++++++++++++
 lib/ipset_hash_ipmark.c                            |  142 ++++++++++++++++
 lib/ipset_hash_ipport.c                            |  147 +++++++++++++++++
 lib/ipset_hash_ipportip.c                          |  158 ++++++++++++++++++
 lib/ipset_hash_ipportnet.c                         |  169 ++++++++++++++++++++
 lib/ipset_hash_net.c                               |  119 ++++++++++++++
 lib/ipset_hash_netiface.c                          |  125 +++++++++++++++
 lib/ipset_hash_netnet.c                            |  125 +++++++++++++++
 lib/ipset_hash_netport.c                           |  132 +++++++++++++++
 lib/ipset_hash_netportnet.c                        |  149 +++++++++++++++++
 lib/parse.c                                        |   21 +++
 lib/print.c                                        |    4 +
 lib/session.c                                      |    4 +
 src/ipset.8                                        |    7 +
 31 files changed, 1494 insertions(+), 13 deletions(-)


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] ipset: add forceadd kernel support for hash set types
  2014-02-01 13:30 [PATCH 0/2] ipset: forceadd support Josh Hunt
@ 2014-02-01 13:30 ` Josh Hunt
  2014-02-14 10:06   ` Jozsef Kadlecsik
  2014-02-01 13:30 ` [PATCH 2/2] ipset: add forceadd userspace " Josh Hunt
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Josh Hunt @ 2014-02-01 13:30 UTC (permalink / raw)
  To: netfilter-devel, kadlec; +Cc: Josh Hunt

Adds a new property for hash set types, where if a set is created
with the 'forceadd' option and the set becomes full the next addition
to the set may succeed and evict a random entry from the set.

To keep overhead low eviction is done very simply. It checks to see
which bucket the new entry would be added. If the bucket's pos value
is non-zero (meaning there's at least one entry in the bucket) it
replaces the first entry in the bucket. If pos is zero, then it continues
down the normal add process.

This property is useful if you have a set for 'ban' lists where it may
not matter if you release some entries from the set early.

Signed-off-by: Josh Hunt <johunt@akamai.com>
---
 kernel/include/uapi/linux/netfilter/ipset/ip_set.h |    1 +
 kernel/net/netfilter/ipset/ip_set_hash_gen.h       |   26 ++++++++++++++++++-
 kernel/net/netfilter/ipset/ip_set_hash_ip.c        |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_ipmark.c    |    3 +-
 kernel/net/netfilter/ipset/ip_set_hash_ipport.c    |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_ipportip.c  |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_net.c       |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_netiface.c  |    4 ++-
 kernel/net/netfilter/ipset/ip_set_hash_netnet.c    |    3 +-
 kernel/net/netfilter/ipset/ip_set_hash_netport.c   |    4 ++-
 .../net/netfilter/ipset/ip_set_hash_netportnet.c   |    4 ++-
 12 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
index c2bae85..0e1478e 100644
--- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -95,6 +95,7 @@ enum {
 	IPSET_ATTR_PROBES,
 	IPSET_ATTR_RESIZE,
 	IPSET_ATTR_SIZE,
+	IPSET_ATTR_FORCEADD,
 	/* Kernel-only */
 	IPSET_ATTR_ELEMENTS,
 	IPSET_ATTR_REFERENCES,
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
index fa259db..b4ca130 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
@@ -263,6 +263,7 @@ struct htype {
 	u32 maxelem;		/* max elements in the hash */
 	u32 elements;		/* current element (vs timeout) */
 	u32 initval;		/* random jhash init value */
+	u8 forceadd;            /* if hash full, attempt to evict one on add */
 #ifdef IP_SET_HASH_WITH_MARKMASK
 	u32 markmask;		/* markmask value for mark mask to store */
 #endif
@@ -633,6 +634,19 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 	bool flag_exist = flags & IPSET_FLAG_EXIST;
 	u32 key, multi = 0;
 
+        if (h->elements >= h->maxelem && h->forceadd) {
+                rcu_read_lock_bh();
+                t = rcu_dereference_bh(h->table);
+                key = HKEY(value, h->initval, t->htable_bits);
+                n = hbucket(t,key);
+                if (n->pos) {
+                        /* Choosing the first entry in the array to replace */
+                        j = 0;
+                        goto reuse_slot;
+                }
+                rcu_read_unlock_bh();
+        }
+
 	if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
 		/* FIXME: when set is full, we slow down here */
 		mtype_expire(set, h, NLEN(set->family), set->dsize);
@@ -923,6 +937,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 		goto nla_put_failure;
 	if (unlikely(ip_set_put_flags(skb, set)))
 		goto nla_put_failure;
+        if ((h->forceadd) && nla_put_u8(skb, IPSET_ATTR_FORCEADD, htonl(h->forceadd)))
+                goto nla_put_failure;
+
 	ipset_nest_end(skb, nested);
 
 	return 0;
@@ -1139,9 +1156,14 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
 				IPSET_TOKEN(HTYPE, 6_gc));
 	}
 
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
+        if (tb[IPSET_ATTR_FORCEADD])
+                h->forceadd = 1;
+        else
+                h->forceadd = 0;
+
+	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p) forceadd %d\n",
 		 set->name, jhash_size(t->htable_bits),
-		 t->htable_bits, h->maxelem, set->data, t);
+		 t->htable_bits, h->maxelem, set->data, t, h->forceadd);
 
 	return 0;
 }
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ip.c b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
index e65fc24..6b97b91 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
@@ -25,7 +25,8 @@
 
 #define IPSET_TYPE_REV_MIN	0
 /*				1	   Counters support */
-#define IPSET_TYPE_REV_MAX	2	/* Comments support */
+/*				2	   Comments support */
+#define IPSET_TYPE_REV_MAX	3	/* Forceadd support */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -284,6 +285,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_NETMASK]	= { .type = NLA_U8  },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c b/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
index 1bf8e85..19304e3 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipmark.c
@@ -25,7 +25,7 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define IPSET_TYPE_REV_MIN	0
-#define IPSET_TYPE_REV_MAX	0
+#define IPSET_TYPE_REV_MAX	1	/* Forceadd support */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Vytas Dauksa <vytas.dauksa@smoothwall.net>");
@@ -290,6 +290,7 @@ static struct ip_set_type hash_ipmark_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
index 525a595..b543b4e 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -27,7 +27,8 @@
 #define IPSET_TYPE_REV_MIN	0
 /*				1    SCTP and UDPLITE support added */
 /*				2    Counters support added */
-#define IPSET_TYPE_REV_MAX	3 /* Comments support added */
+/*				3    Comments support added */
+#define IPSET_TYPE_REV_MAX	4 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -356,6 +357,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
index f563663..3379389 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -27,7 +27,8 @@
 #define IPSET_TYPE_REV_MIN	0
 /*				1    SCTP and UDPLITE support added */
 /*				2    Counters support added */
-#define IPSET_TYPE_REV_MAX	3 /* Comments support added */
+/*				3    Comments support added */
+#define IPSET_TYPE_REV_MAX	4 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -367,6 +368,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 5d87fe8..86d91ef 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -29,7 +29,8 @@
 /*				2    Range as input support for IPv4 added */
 /*				3    nomatch flag support added */
 /*				4    Counters support added */
-#define IPSET_TYPE_REV_MAX	5 /* Comments support added */
+/*				5    Comments support added */
+#define IPSET_TYPE_REV_MAX	6 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -523,6 +524,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_net.c b/kernel/net/netfilter/ipset/ip_set_hash_net.c
index 8295cf4..e96530a 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_net.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_net.c
@@ -26,7 +26,8 @@
 /*				1    Range as input support for IPv4 added */
 /*				2    nomatch flag support added */
 /*				3    Counters support added */
-#define IPSET_TYPE_REV_MAX	4 /* Comments support added */
+/*				4    Comments support added */
+#define IPSET_TYPE_REV_MAX	5 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -366,6 +367,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
index 788825b..e318e09 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -27,7 +27,8 @@
 /*				1    nomatch flag support added */
 /*				2    /0 support added */
 /*				3    Counters support added */
-#define IPSET_TYPE_REV_MAX	4 /* Comments support added */
+/*				4    Comments support added */
+#define IPSET_TYPE_REV_MAX	5 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -604,6 +605,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netnet.c b/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
index 4e7261d..333ad77 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -24,7 +24,7 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define IPSET_TYPE_REV_MIN	0
-#define IPSET_TYPE_REV_MAX	0
+#define IPSET_TYPE_REV_MAX	1 	/* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
@@ -448,6 +448,7 @@ static struct ip_set_type hash_netnet_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netport.c b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
index 7097fb0..9aff329 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netport.c
@@ -28,7 +28,8 @@
 /*				2    Range as input support for IPv4 added */
 /*				3    nomatch flag support added */
 /*				4    Counters support added */
-#define IPSET_TYPE_REV_MAX	5 /* Comments support added */
+/*				5    Comments support added */
+#define IPSET_TYPE_REV_MAX	6 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -474,6 +475,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c b/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
index 703d119..bdffc9e 100644
--- a/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/kernel/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -25,7 +25,8 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define IPSET_TYPE_REV_MIN	0
-#define IPSET_TYPE_REV_MAX	0 /* Comments support added */
+/*				0    Comments support added */
+#define IPSET_TYPE_REV_MAX	1 /* Forceadd support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
@@ -549,6 +550,7 @@ static struct ip_set_type hash_netportnet_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] ipset: add forceadd userspace support for hash set types
  2014-02-01 13:30 [PATCH 0/2] ipset: forceadd support Josh Hunt
  2014-02-01 13:30 ` [PATCH 1/2] ipset: add forceadd kernel support for hash set types Josh Hunt
@ 2014-02-01 13:30 ` Josh Hunt
  2014-02-14 10:10   ` Jozsef Kadlecsik
  2014-02-15 15:07 ` [PATCH 0/2] ipset: forceadd support Bourne Without
  2014-02-15 15:09 ` Bourne Without
  3 siblings, 1 reply; 8+ messages in thread
From: Josh Hunt @ 2014-02-01 13:30 UTC (permalink / raw)
  To: netfilter-devel, kadlec; +Cc: Josh Hunt

The userspace side of the forceadd changes.

Signed-off-by: Josh Hunt <johunt@akamai.com>
---
 include/libipset/data.h         |    4 +-
 include/libipset/linux_ip_set.h |    1 +
 include/libipset/parse.h        |    3 +
 lib/data.c                      |    8 ++
 lib/debug.c                     |    1 +
 lib/ipset_hash_ip.c             |  123 ++++++++++++++++++++++++++++
 lib/ipset_hash_ipmark.c         |  142 ++++++++++++++++++++++++++++++++
 lib/ipset_hash_ipport.c         |  147 ++++++++++++++++++++++++++++++++++
 lib/ipset_hash_ipportip.c       |  158 ++++++++++++++++++++++++++++++++++++
 lib/ipset_hash_ipportnet.c      |  169 +++++++++++++++++++++++++++++++++++++++
 lib/ipset_hash_net.c            |  119 +++++++++++++++++++++++++++
 lib/ipset_hash_netiface.c       |  125 +++++++++++++++++++++++++++++
 lib/ipset_hash_netnet.c         |  125 +++++++++++++++++++++++++++++
 lib/ipset_hash_netport.c        |  132 ++++++++++++++++++++++++++++++
 lib/ipset_hash_netportnet.c     |  149 ++++++++++++++++++++++++++++++++++
 lib/parse.c                     |   21 +++++
 lib/print.c                     |    4 +
 lib/session.c                   |    4 +
 src/ipset.8                     |    7 ++
 19 files changed, 1441 insertions(+), 1 deletions(-)

diff --git a/include/libipset/data.h b/include/libipset/data.h
index 3a26b1e..06ece1e 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -36,6 +36,7 @@ enum ipset_opt {
 	IPSET_OPT_PROBES,
 	IPSET_OPT_RESIZE,
 	IPSET_OPT_SIZE,
+	IPSET_OPT_FORCEADD,
 	/* Create-specific options, filled out by the kernel */
 	IPSET_OPT_ELEMENTS,
 	IPSET_OPT_REFERENCES,
@@ -94,7 +95,8 @@ enum ipset_opt {
 	| IPSET_FLAG(IPSET_OPT_RESIZE)	\
 	| IPSET_FLAG(IPSET_OPT_SIZE)	\
 	| IPSET_FLAG(IPSET_OPT_COUNTERS)\
-	| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT))
+	| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)\
+	| IPSET_FLAG(IPSET_OPT_FORCEADD))
 
 #define IPSET_ADT_FLAGS			\
 	(IPSET_FLAG(IPSET_OPT_IP)	\
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index c2bae85..0e1478e 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -95,6 +95,7 @@ enum {
 	IPSET_ATTR_PROBES,
 	IPSET_ATTR_RESIZE,
 	IPSET_ATTR_SIZE,
+	IPSET_ATTR_FORCEADD,
 	/* Kernel-only */
 	IPSET_ATTR_ELEMENTS,
 	IPSET_ATTR_REFERENCES,
diff --git a/include/libipset/parse.h b/include/libipset/parse.h
index 55981f2..93af5c3 100644
--- a/include/libipset/parse.h
+++ b/include/libipset/parse.h
@@ -103,6 +103,9 @@ extern int ipset_parse_elem(struct ipset_session *session,
 extern int ipset_call_parser(struct ipset_session *session,
 			     const struct ipset_arg *arg,
 			     const char *str);
+extern int ipset_parse_forceadd(struct ipset_session *session,
+                               enum ipset_opt opt, const char *str);
+
 
 /* Compatibility parser functions */
 extern int ipset_parse_iptimeout(struct ipset_session *session,
diff --git a/lib/data.c b/lib/data.c
index 48ec98a..a841d7f 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -57,6 +57,7 @@ struct ipset_data {
 			uint32_t markmask;
 			uint32_t gc;
 			uint32_t size;
+			uint8_t forceadd;
 			/* Filled out by kernel */
 			uint32_t references;
 			uint32_t elements;
@@ -309,6 +310,10 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
 	case IPSET_OPT_CREATE_COMMENT:
 		cadt_flag_type_attr(data, opt, IPSET_FLAG_WITH_COMMENT);
 		break;
+        case IPSET_OPT_FORCEADD:
+		data->create.forceadd = *(const uint8_t *) value;
+		break;
+
 	/* Create-specific options, filled out by the kernel */
 	case IPSET_OPT_ELEMENTS:
 		data->create.elements = *(const uint32_t *) value;
@@ -481,6 +486,9 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
 		return &data->create.resize;
 	case IPSET_OPT_SIZE:
 		return &data->create.size;
+	case IPSET_OPT_FORCEADD:
+		return &data->create.forceadd;
+
 	/* Create-specific options, filled out by the kernel */
 	case IPSET_OPT_ELEMENTS:
 		return &data->create.elements;
diff --git a/lib/debug.c b/lib/debug.c
index 1240e22..14249e5 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -46,6 +46,7 @@ static const struct ipset_attrname createattr2name[] = {
 	[IPSET_ATTR_ELEMENTS]	= { .name = "ELEMENTS" },
 	[IPSET_ATTR_REFERENCES]	= { .name = "REFERENCES" },
 	[IPSET_ATTR_MEMSIZE]	= { .name = "MEMSIZE" },
+	[IPSET_ATTR_FORCEADD]	= { .name = "FORCEADD" },
 };
 
 static const struct ipset_attrname adtattr2name[] = {
diff --git a/lib/ipset_hash_ip.c b/lib/ipset_hash_ip.c
index 45185ec..cde0781 100644
--- a/lib/ipset_hash_ip.c
+++ b/lib/ipset_hash_ip.c
@@ -383,10 +383,133 @@ static struct ipset_type ipset_hash_ip2 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_ip_create_args3[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "netmask", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_NETMASK,
+	  .parse = ipset_parse_netmask,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "forceadd", NULL },
+	  .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+	  .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	/* Ignored options: backward compatibilty */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "gc", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_GC,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ },
+};
+
+static const char hash_ip_usage3[] =
+"create SETNAME hash:ip\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [netmask CIDR] [timeout VALUE]\n"
+"               [counters] [comment] [forceadd]\n"
+"add    SETNAME IP [timeout VALUE]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP\n"
+"test   SETNAME IP\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      is supported for IPv4.\n";
+
+static struct ipset_type ipset_hash_ip3 = {
+	.name = "hash:ip",
+	.alias = { "iphash", NULL },
+	.revision = 3,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_ONE,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_single6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_ip_create_args3,
+		[IPSET_ADD] = hash_ip_add_args2,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_NETMASK)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
+	},
+
+	.usage = hash_ip_usage3,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_ip0);
 	ipset_type_add(&ipset_hash_ip1);
 	ipset_type_add(&ipset_hash_ip2);
+	ipset_type_add(&ipset_hash_ip3);
 }
diff --git a/lib/ipset_hash_ipmark.c b/lib/ipset_hash_ipmark.c
index 922e6c0..b352b08 100644
--- a/lib/ipset_hash_ipmark.c
+++ b/lib/ipset_hash_ipmark.c
@@ -166,8 +166,150 @@ static struct ipset_type ipset_hash_ipmark0 = {
 	.description = "initial revision",
 };
 
+static const struct ipset_arg hash_ipmark_create_args1[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "markmask", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MARKMASK,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_mark,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "forceadd", NULL },
+	  .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+	  .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+	},
+	/* Backward compatibility */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "from", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "to", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP_TO,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "network", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ },
+};
+
+static const char hash_ipmark_usage1[] =
+"create SETNAME hash:ip,mark\n"
+"		[family inet|inet6] [markmask VALUE]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP,MARK [timeout VALUE]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP,MARK\n"
+"test   SETNAME IP,MARK\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname).\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      is supported for IPv4.\n"
+"      Adding/deleting single mark element\n"
+"      is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_ipmark1 = {
+	.name = "hash:ip,mark",
+	.alias = { "ipmarkhash", NULL },
+	.revision = 1,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_TWO,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_single6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_mark,
+			.print = ipset_print_mark,
+			.opt = IPSET_OPT_MARK
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_ipmark_create_args1,
+		[IPSET_ADD] = hash_ipmark_add_args0,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_MARK),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_MARK),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_MARK),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_MARKMASK)
+			| IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_MARK)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_MARK),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_MARK),
+	},
+
+	.usage = hash_ipmark_usage1,
+	.description = "forceadd support"
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_ipmark0);
+	ipset_type_add(&ipset_hash_ipmark1);
 }
diff --git a/lib/ipset_hash_ipport.c b/lib/ipset_hash_ipport.c
index c9dc4c1..0413697 100644
--- a/lib/ipset_hash_ipport.c
+++ b/lib/ipset_hash_ipport.c
@@ -454,10 +454,157 @@ static struct ipset_type ipset_hash_ipport3 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_ipport_create_args4[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	/* Backward compatibility */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "from", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "to", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP_TO,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "network", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ },
+};
+
+static const char hash_ipport_usage4[] =
+"create SETNAME hash:ip,port\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP,PROTO:PORT [timeout VALUE]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP,PROTO:PORT\n"
+"test   SETNAME IP,PROTO:PORT\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname).\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      is supported for IPv4.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_ipport4 = {
+	.name = "hash:ip,port",
+	.alias = { "ipporthash", NULL },
+	.revision = 4,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_TWO,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_single6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_proto_port,
+			.print = ipset_print_proto_port,
+			.opt = IPSET_OPT_PORT
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_ipport_create_args4,
+		[IPSET_ADD] = hash_ipport_add_args3,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO),
+	},
+
+	.usage = hash_ipport_usage4,
+	.usagefn = ipset_port_usage,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_ipport1);
 	ipset_type_add(&ipset_hash_ipport2);
 	ipset_type_add(&ipset_hash_ipport3);
+	ipset_type_add(&ipset_hash_ipport4);
 }
diff --git a/lib/ipset_hash_ipportip.c b/lib/ipset_hash_ipportip.c
index 9ae4f2d..0104fc5 100644
--- a/lib/ipset_hash_ipportip.c
+++ b/lib/ipset_hash_ipportip.c
@@ -487,10 +487,168 @@ static struct ipset_type ipset_hash_ipportip3 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_ipportip_create_args4[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	/* Backward compatibility */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "from", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "to", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP_TO,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "network", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ },
+};
+
+static const char hash_ipportip_usage4[] =
+"create SETNAME hash:ip,port,ip\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP,PROTO:PORT,IP [timeout VALUE]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP,PROTO:PORT,IP\n"
+"test   SETNAME IP,PROTO:PORT,IP\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname).\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      in the first IP component is supported for IPv4.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_ipportip4 = {
+	.name = "hash:ip,port,ip",
+	.alias = { "ipportiphash", NULL },
+	.revision = 4,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_THREE,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_single6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_proto_port,
+			.print = ipset_print_proto_port,
+			.opt = IPSET_OPT_PORT
+		},
+		[IPSET_DIM_THREE - 1] = {
+			.parse = ipset_parse_single_ip,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP2
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_ipportip_create_args4,
+		[IPSET_ADD] = hash_ipportip_add_args3,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+	},
+
+	.usage = hash_ipportip_usage4,
+	.usagefn = ipset_port_usage,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_ipportip1);
 	ipset_type_add(&ipset_hash_ipportip2);
 	ipset_type_add(&ipset_hash_ipportip3);
+	ipset_type_add(&ipset_hash_ipportip4);
 }
diff --git a/lib/ipset_hash_ipportnet.c b/lib/ipset_hash_ipportnet.c
index 4baabe5..26987db 100644
--- a/lib/ipset_hash_ipportnet.c
+++ b/lib/ipset_hash_ipportnet.c
@@ -738,6 +738,174 @@ static struct ipset_type ipset_hash_ipportnet5 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_ipportnet_create_args6[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	/* Backward compatibility */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "from", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "to", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP_TO,
+	  .parse = ipset_parse_ignored,
+	},
+	{ .name = { "network", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_IP,
+	  .parse = ipset_parse_ignored,
+	},
+	{ },
+};
+
+static const char hash_ipportnet_usage6[] =
+"create SETNAME hash:ip,port,net\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP,PROTO:PORT,IP[/CIDR] [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP,PROTO:PORT,IP[/CIDR]\n"
+"test   SETNAME IP,PROTO:PORT,IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP are valid IPv4 or IPv6 addresses (or hostnames),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      in both IP components are supported for IPv4.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_ipportnet6 = {
+	.name = "hash:ip,port,net",
+	.alias = { "ipportnethash", NULL },
+	.revision = 6,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_THREE,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_single6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_proto_port,
+			.print = ipset_print_proto_port,
+			.opt = IPSET_OPT_PORT
+		},
+		[IPSET_DIM_THREE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP2
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_ipportnet_create_args6,
+		[IPSET_ADD] = hash_ipportnet_add_args5,
+		[IPSET_TEST] = hash_ipportnet_test_args5,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_ipportnet_usage6,
+	.usagefn = ipset_port_usage,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
@@ -746,4 +914,5 @@ void _init(void)
 	ipset_type_add(&ipset_hash_ipportnet3);
 	ipset_type_add(&ipset_hash_ipportnet4);
 	ipset_type_add(&ipset_hash_ipportnet5);
+	ipset_type_add(&ipset_hash_ipportnet6);
 }
diff --git a/lib/ipset_hash_net.c b/lib/ipset_hash_net.c
index 01da722..bb2d2b8 100644
--- a/lib/ipset_hash_net.c
+++ b/lib/ipset_hash_net.c
@@ -510,6 +510,124 @@ static struct ipset_type ipset_hash_net4 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_net_create_args5[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	/* Ignored options: backward compatibilty */
+	{ .name = { "probes", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_PROBES,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ .name = { "resize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_RESIZE,
+	  .parse = ipset_parse_ignored,		.print = ipset_print_number,
+	},
+	{ },
+};
+
+static const char hash_net_usage5[] =
+"create SETNAME hash:net\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO\n"
+"test   SETNAME IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP is an IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      IP range is not supported with IPv6.\n";
+
+static struct ipset_type ipset_hash_net5 = {
+	.name = "hash:net",
+	.alias = { "nethash", NULL },
+	.revision = 5,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_ONE,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_net_create_args5,
+		[IPSET_ADD] = hash_net_add_args4,
+		[IPSET_TEST] = hash_net_test_args4,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_net_usage5,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
@@ -518,4 +636,5 @@ void _init(void)
 	ipset_type_add(&ipset_hash_net2);
 	ipset_type_add(&ipset_hash_net3);
 	ipset_type_add(&ipset_hash_net4);
+	ipset_type_add(&ipset_hash_net5);
 }
diff --git a/lib/ipset_hash_netiface.c b/lib/ipset_hash_netiface.c
index ed59a91..bf8e804 100644
--- a/lib/ipset_hash_netiface.c
+++ b/lib/ipset_hash_netiface.c
@@ -550,6 +550,130 @@ static struct ipset_type ipset_hash_netiface4 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_netiface_create_args5[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	{ },
+};
+
+static const char hash_netiface_usage5[] =
+"create SETNAME hash:net,iface\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO,[physdev:]IFACE\n"
+"test   SETNAME IP[/CIDR],[physdev:]IFACE\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements with IPv4 is supported.\n";
+
+static struct ipset_type ipset_hash_netiface5 = {
+	.name = "hash:net,iface",
+	.alias = { "netifacehash", NULL },
+	.revision = 5,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_TWO,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_iface,
+			.print = ipset_print_iface,
+			.opt = IPSET_OPT_IFACE
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_netiface_create_args5,
+		[IPSET_ADD] = hash_netiface_add_args4,
+		[IPSET_TEST] = hash_netiface_test_args4,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IFACE),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IFACE),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IFACE),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_IFACE)
+			| IPSET_FLAG(IPSET_OPT_PHYSDEV)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_IFACE)
+			| IPSET_FLAG(IPSET_OPT_PHYSDEV),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_IFACE)
+			| IPSET_FLAG(IPSET_OPT_PHYSDEV)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_netiface_usage5,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
@@ -558,4 +682,5 @@ void _init(void)
 	ipset_type_add(&ipset_hash_netiface2);
 	ipset_type_add(&ipset_hash_netiface3);
 	ipset_type_add(&ipset_hash_netiface4);
+	ipset_type_add(&ipset_hash_netiface5);
 }
diff --git a/lib/ipset_hash_netnet.c b/lib/ipset_hash_netnet.c
index 0e617af..4568283 100644
--- a/lib/ipset_hash_netnet.c
+++ b/lib/ipset_hash_netnet.c
@@ -161,8 +161,133 @@ static struct ipset_type ipset_hash_netnet0 = {
 	.description = "initial revision",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_netnet_create_args1[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	{ },
+};
+
+static const char hash_netnet_usage1[] =
+"create SETNAME hash:net,net\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [forceadd]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO\n"
+"test   SETNAME IP[/CIDR],IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP is an IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      IP range is not supported with IPv6.\n";
+
+static struct ipset_type ipset_hash_netnet1 = {
+	.name = "hash:net,net",
+	.alias = { "netnethash", NULL },
+	.revision = 1,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_TWO,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP2
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_netnet_create_args1,
+		[IPSET_ADD] = hash_netnet_add_args0,
+		[IPSET_TEST] = hash_netnet_test_args0,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_netnet_usage1,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_netnet0);
+	ipset_type_add(&ipset_hash_netnet1);
 }
diff --git a/lib/ipset_hash_netport.c b/lib/ipset_hash_netport.c
index 3a41456..314e303 100644
--- a/lib/ipset_hash_netport.c
+++ b/lib/ipset_hash_netport.c
@@ -594,6 +594,137 @@ static struct ipset_type ipset_hash_netport5 = {
 	.description = "comment support",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_netport_create_args6[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	{ },
+};
+
+static const char hash_netport_usage6[] =
+"create SETNAME hash:net,port\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP[/CIDR]|FROM-TO,PROTO:PORT\n"
+"test   SETNAME IP[/CIDR],PROTO:PORT\n\n"
+"where depending on the INET family\n"
+"      IP is a valid IPv4 or IPv6 address (or hostname),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements with IPv4 is supported.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_netport6 = {
+	.name = "hash:net,port",
+	.alias = { "netporthash", NULL },
+	.revision = 6,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_TWO,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_proto_port,
+			.print = ipset_print_proto_port,
+			.opt = IPSET_OPT_PORT
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_netport_create_args6,
+		[IPSET_ADD] = hash_netport_add_args5,
+		[IPSET_TEST] = hash_netport_test_args5,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_PORT),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_netport_usage6,
+	.usagefn = ipset_port_usage,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
@@ -602,4 +733,5 @@ void _init(void)
 	ipset_type_add(&ipset_hash_netport3);
 	ipset_type_add(&ipset_hash_netport4);
 	ipset_type_add(&ipset_hash_netport5);
+	ipset_type_add(&ipset_hash_netport6);
 }
diff --git a/lib/ipset_hash_netportnet.c b/lib/ipset_hash_netportnet.c
index 728c4a3..3099f53 100644
--- a/lib/ipset_hash_netportnet.c
+++ b/lib/ipset_hash_netportnet.c
@@ -184,8 +184,157 @@ static struct ipset_type ipset_hash_netportnet0 = {
 	.description = "initial revision",
 };
 
+/* Parse commandline arguments */
+static const struct ipset_arg hash_netportnet_create_args1[] = {
+	{ .name = { "family", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,		.print = ipset_print_family,
+	},
+	/* Alias: family inet */
+	{ .name = { "-4", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	/* Alias: family inet6 */
+	{ .name = { "-6", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
+	  .parse = ipset_parse_family,
+	},
+	{ .name = { "hashsize", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "maxelem", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
+	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
+	},
+	{ .name = { "timeout", NULL },
+	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
+	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
+	},
+	{ .name = { "counters", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+	{ .name = { "comment", NULL },
+	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
+	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
+	},
+        { .name = { "forceadd", NULL },
+          .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
+          .parse = ipset_parse_forceadd,        .print = ipset_print_number,
+        },
+	{ },
+};
+
+static const char hash_netportnet_usage1[] =
+"create SETNAME hash:net,port,net\n"
+"		[family inet|inet6]\n"
+"               [hashsize VALUE] [maxelem VALUE]\n"
+"               [timeout VALUE] [counters] [comment]\n"
+"		[forceadd]\n"
+"add    SETNAME IP[/CIDR],PROTO:PORT,IP[/CIDR] [timeout VALUE] [nomatch]\n"
+"               [packets VALUE] [bytes VALUE] [comment \"string\"]\n"
+"del    SETNAME IP[/CIDR],PROTO:PORT,IP[/CIDR]\n"
+"test   SETNAME IP[/CIDR],PROTO:PORT,IP[/CIDR]\n\n"
+"where depending on the INET family\n"
+"      IP are valid IPv4 or IPv6 addresses (or hostnames),\n"
+"      CIDR is a valid IPv4 or IPv6 CIDR prefix.\n"
+"      Adding/deleting multiple elements in IP/CIDR or FROM-TO form\n"
+"      in both IP components are supported for IPv4.\n"
+"      Adding/deleting multiple elements with TCP/SCTP/UDP/UDPLITE\n"
+"      port range is supported both for IPv4 and IPv6.\n";
+
+static struct ipset_type ipset_hash_netportnet1 = {
+	.name = "hash:net,port,net",
+	.alias = { "netportnethash", NULL },
+	.revision = 1,
+	.family = NFPROTO_IPSET_IPV46,
+	.dimension = IPSET_DIM_THREE,
+	.elem = {
+		[IPSET_DIM_ONE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP
+		},
+		[IPSET_DIM_TWO - 1] = {
+			.parse = ipset_parse_proto_port,
+			.print = ipset_print_proto_port,
+			.opt = IPSET_OPT_PORT
+		},
+		[IPSET_DIM_THREE - 1] = {
+			.parse = ipset_parse_ip4_net6,
+			.print = ipset_print_ip,
+			.opt = IPSET_OPT_IP2
+		},
+	},
+	.args = {
+		[IPSET_CREATE] = hash_netportnet_create_args1,
+		[IPSET_ADD] = hash_netportnet_add_args0,
+		[IPSET_TEST] = hash_netportnet_test_args0,
+	},
+	.mandatory = {
+		[IPSET_CREATE] = 0,
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2),
+	},
+	.full = {
+		[IPSET_CREATE] = IPSET_FLAG(IPSET_OPT_HASHSIZE)
+			| IPSET_FLAG(IPSET_OPT_MAXELEM)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_COUNTERS)
+			| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)
+			| IPSET_FLAG(IPSET_OPT_FORCEADD),
+		[IPSET_ADD] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO)
+			| IPSET_FLAG(IPSET_OPT_TIMEOUT)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH)
+			| IPSET_FLAG(IPSET_OPT_PACKETS)
+			| IPSET_FLAG(IPSET_OPT_BYTES)
+			| IPSET_FLAG(IPSET_OPT_ADT_COMMENT),
+		[IPSET_DEL] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_IP_TO)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PORT_TO)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_IP2_TO),
+		[IPSET_TEST] = IPSET_FLAG(IPSET_OPT_IP)
+			| IPSET_FLAG(IPSET_OPT_CIDR)
+			| IPSET_FLAG(IPSET_OPT_PORT)
+			| IPSET_FLAG(IPSET_OPT_PROTO)
+			| IPSET_FLAG(IPSET_OPT_IP2)
+			| IPSET_FLAG(IPSET_OPT_CIDR2)
+			| IPSET_FLAG(IPSET_OPT_NOMATCH),
+	},
+
+	.usage = hash_netportnet_usage1,
+	.usagefn = ipset_port_usage,
+	.description = "forceadd support",
+};
+
 void _init(void);
 void _init(void)
 {
 	ipset_type_add(&ipset_hash_netportnet0);
+	ipset_type_add(&ipset_hash_netportnet1);
 }
diff --git a/lib/parse.c b/lib/parse.c
index f1c1f0e..1953e9e 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -1988,3 +1988,24 @@ out:
 	free(saved);
 	return ret;
 }
+
+/**
+ * ipset_parse_forceadd - parse forceadd set option
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse forceadd arg
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_forceadd(struct ipset_session *session,
+		enum ipset_opt opt, const char *str)
+{
+	uint8_t force = 1;
+        assert(session);
+
+        return ipset_session_data_set(session, opt, &force);
+}
diff --git a/lib/print.c b/lib/print.c
index f81c074..86dfa03 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -903,6 +903,10 @@ ipset_print_data(char *buf, unsigned int len,
 	case IPSET_OPT_SIZE:
 		size = ipset_print_number(buf, len, data, opt, env);
 		break;
+	case IPSET_OPT_FORCEADD:
+		size = snprintf(buf, len, "forceadd", NULL);
+		break;
+
 	default:
 		return -1;
 	}
diff --git a/lib/session.c b/lib/session.c
index d2957a5..095cbec 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -413,6 +413,10 @@ static const struct ipset_attr_policy create_attrs[] = {
 		.type = MNL_TYPE_U32,
 		.opt = IPSET_OPT_MEMSIZE,
 	},
+	[IPSET_ATTR_FORCEADD] = {
+		.type = MNL_TYPE_U8,
+		.opt = IPSET_OPT_FORCEADD,
+	},
 };
 
 static const struct ipset_attr_policy adt_attrs[] = {
diff --git a/src/ipset.8 b/src/ipset.8
index eeda9e7..6c9a0f5 100644
--- a/src/ipset.8
+++ b/src/ipset.8
@@ -327,6 +327,13 @@ ipset add foo 192.168.1.1/24 comment "allow access to SMB share on \\\\\\\\files
 .IP
 the above would appear as: "allow access to SMB share on \\\\fileserv\\"
 .PP
+.SS forceadd
+All hash set types support the optional \fBforceadd\fR parameter when creating a set.
+When sets created with this option become full the next addition to the set may
+succeed and evict a random entry from the set.
+.IP
+ipset create foo hash:ip forceadd
+.PP
 .SH "SET TYPES"
 .SS bitmap:ip
 The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] ipset: add forceadd kernel support for hash set types
  2014-02-01 13:30 ` [PATCH 1/2] ipset: add forceadd kernel support for hash set types Josh Hunt
@ 2014-02-14 10:06   ` Jozsef Kadlecsik
  0 siblings, 0 replies; 8+ messages in thread
From: Jozsef Kadlecsik @ 2014-02-14 10:06 UTC (permalink / raw)
  To: Josh Hunt; +Cc: netfilter-devel

Hi Josh,

On Sat, 1 Feb 2014, Josh Hunt wrote:

> Adds a new property for hash set types, where if a set is created
> with the 'forceadd' option and the set becomes full the next addition
> to the set may succeed and evict a random entry from the set.
> 
> To keep overhead low eviction is done very simply. It checks to see
> which bucket the new entry would be added. If the bucket's pos value
> is non-zero (meaning there's at least one entry in the bucket) it
> replaces the first entry in the bucket. If pos is zero, then it continues
> down the normal add process.

I think it's a great feature and the overhead is really low - I'm happy 
to apply it. I have problem only with the implementation on how the single 
flag is handled and passed between userspace and kernel. Please see my 
comments below.
 
> This property is useful if you have a set for 'ban' lists where it may
> not matter if you release some entries from the set early.
> 
> Signed-off-by: Josh Hunt <johunt@akamai.com>
> ---
>  kernel/include/uapi/linux/netfilter/ipset/ip_set.h |    1 +
>  kernel/net/netfilter/ipset/ip_set_hash_gen.h       |   26 ++++++++++++++++++-
>  kernel/net/netfilter/ipset/ip_set_hash_ip.c        |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_ipmark.c    |    3 +-
>  kernel/net/netfilter/ipset/ip_set_hash_ipport.c    |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_ipportip.c  |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_net.c       |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_netiface.c  |    4 ++-
>  kernel/net/netfilter/ipset/ip_set_hash_netnet.c    |    3 +-
>  kernel/net/netfilter/ipset/ip_set_hash_netport.c   |    4 ++-
>  .../net/netfilter/ipset/ip_set_hash_netportnet.c   |    4 ++-
>  12 files changed, 53 insertions(+), 12 deletions(-)
> 
> diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> index c2bae85..0e1478e 100644
> --- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> +++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
> @@ -95,6 +95,7 @@ enum {
>  	IPSET_ATTR_PROBES,
>  	IPSET_ATTR_RESIZE,
>  	IPSET_ATTR_SIZE,
> +	IPSET_ATTR_FORCEADD,
>  	/* Kernel-only */
>  	IPSET_ATTR_ELEMENTS,
>  	IPSET_ATTR_REFERENCES,

Do not introduce a new attribute just for a flag: there is the 
IPSET_ATTR_CADT_FLAGS attribute for this with a lot of unused bits. So 
please add a new flag to "enum ipset_cadt_flags" instead which is for 
transferring the flag. In order to store it, I added the new "flags" 
member to struct ip_set in a hole in the structure. The corresponding in 
kernel flag should be added to "enum ipset_create_flags" by replacing 
IPSET_CREATE_FLAG_NONE with IPSET_CREATE_FLAG_FORCEADD or something like 
that.

> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_gen.h b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> index fa259db..b4ca130 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_gen.h
> @@ -263,6 +263,7 @@ struct htype {
>  	u32 maxelem;		/* max elements in the hash */
>  	u32 elements;		/* current element (vs timeout) */
>  	u32 initval;		/* random jhash init value */
> +	u8 forceadd;            /* if hash full, attempt to evict one on add */

With the modifications above this is not needed anymore.

>  #ifdef IP_SET_HASH_WITH_MARKMASK
>  	u32 markmask;		/* markmask value for mark mask to store */
>  #endif
> @@ -633,6 +634,19 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
>  	bool flag_exist = flags & IPSET_FLAG_EXIST;
>  	u32 key, multi = 0;
>  
> +        if (h->elements >= h->maxelem && h->forceadd) {

Here you can check whether the in-kernel flag is set in set->flags.

> +                rcu_read_lock_bh();
> +                t = rcu_dereference_bh(h->table);
> +                key = HKEY(value, h->initval, t->htable_bits);
> +                n = hbucket(t,key);
> +                if (n->pos) {
> +                        /* Choosing the first entry in the array to replace */
> +                        j = 0;
> +                        goto reuse_slot;
> +                }
> +                rcu_read_unlock_bh();
> +        }
> +
>  	if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
>  		/* FIXME: when set is full, we slow down here */
>  		mtype_expire(set, h, NLEN(set->family), set->dsize);
> @@ -923,6 +937,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
>  		goto nla_put_failure;
>  	if (unlikely(ip_set_put_flags(skb, set)))
>  		goto nla_put_failure;
> +        if ((h->forceadd) && nla_put_u8(skb, IPSET_ATTR_FORCEADD, htonl(h->forceadd)))
> +                goto nla_put_failure;
> +

As it's a flag, please remove this and extend the ip_set_put_flags
function: when the in-kernel flag is set, set the appropriate
new flag from enum ipset_cadt_flags in the IPSET_ATTR_CADT_FLAGS
attribute.

>  	ipset_nest_end(skb, nested);
>  
>  	return 0;
> @@ -1139,9 +1156,14 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
>  				IPSET_TOKEN(HTYPE, 6_gc));
>  	}
>  
> -	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
> +        if (tb[IPSET_ATTR_FORCEADD])
> +                h->forceadd = 1;
> +        else
> +                h->forceadd = 0;
> +
> +	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p) forceadd %d\n",
>  		 set->name, jhash_size(t->htable_bits),
> -		 t->htable_bits, h->maxelem, set->data, t);
> +		 t->htable_bits, h->maxelem, set->data, t, h->forceadd);

Remove these too and move the code into ip_set_elem_len (the function
should be better named, I know): if the transport flag is set in
the IPSET_ATTR_CADT_FLAGS attribute, set the corresponding in-kernel flag 
in set->flags.

That way the core handles all the flag handling and converting.

>  
>  	return 0;
>  }
> diff --git a/kernel/net/netfilter/ipset/ip_set_hash_ip.c b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> index e65fc24..6b97b91 100644
> --- a/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> +++ b/kernel/net/netfilter/ipset/ip_set_hash_ip.c
> @@ -25,7 +25,8 @@
>  
>  #define IPSET_TYPE_REV_MIN	0
>  /*				1	   Counters support */
> -#define IPSET_TYPE_REV_MAX	2	/* Comments support */
> +/*				2	   Comments support */
> +#define IPSET_TYPE_REV_MAX	3	/* Forceadd support */
>  
>  MODULE_LICENSE("GPL");
>  MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
> @@ -284,6 +285,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
>  		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
>  		[IPSET_ATTR_NETMASK]	= { .type = NLA_U8  },
>  		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
> +		[IPSET_ATTR_FORCEADD]   = { .type = NLA_U8 },

The attribute can thus be removed from all hash types.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] ipset: add forceadd userspace support for hash set types
  2014-02-01 13:30 ` [PATCH 2/2] ipset: add forceadd userspace " Josh Hunt
@ 2014-02-14 10:10   ` Jozsef Kadlecsik
  2014-02-14 10:24     ` Jozsef Kadlecsik
  0 siblings, 1 reply; 8+ messages in thread
From: Jozsef Kadlecsik @ 2014-02-14 10:10 UTC (permalink / raw)
  To: Josh Hunt; +Cc: netfilter-devel

On Sat, 1 Feb 2014, Josh Hunt wrote:

> The userspace side of the forceadd changes.
> 
> Signed-off-by: Josh Hunt <johunt@akamai.com>
> ---
>  include/libipset/data.h         |    4 +-
>  include/libipset/linux_ip_set.h |    1 +
>  include/libipset/parse.h        |    3 +
>  lib/data.c                      |    8 ++
>  lib/debug.c                     |    1 +
>  lib/ipset_hash_ip.c             |  123 ++++++++++++++++++++++++++++
>  lib/ipset_hash_ipmark.c         |  142 ++++++++++++++++++++++++++++++++
>  lib/ipset_hash_ipport.c         |  147 ++++++++++++++++++++++++++++++++++
>  lib/ipset_hash_ipportip.c       |  158 ++++++++++++++++++++++++++++++++++++
>  lib/ipset_hash_ipportnet.c      |  169 +++++++++++++++++++++++++++++++++++++++
>  lib/ipset_hash_net.c            |  119 +++++++++++++++++++++++++++
>  lib/ipset_hash_netiface.c       |  125 +++++++++++++++++++++++++++++
>  lib/ipset_hash_netnet.c         |  125 +++++++++++++++++++++++++++++
>  lib/ipset_hash_netport.c        |  132 ++++++++++++++++++++++++++++++
>  lib/ipset_hash_netportnet.c     |  149 ++++++++++++++++++++++++++++++++++
>  lib/parse.c                     |   21 +++++
>  lib/print.c                     |    4 +
>  lib/session.c                   |    4 +
>  src/ipset.8                     |    7 ++
>  19 files changed, 1441 insertions(+), 1 deletions(-)
> 
> diff --git a/include/libipset/data.h b/include/libipset/data.h
> index 3a26b1e..06ece1e 100644
> --- a/include/libipset/data.h
> +++ b/include/libipset/data.h
> @@ -36,6 +36,7 @@ enum ipset_opt {
>  	IPSET_OPT_PROBES,
>  	IPSET_OPT_RESIZE,
>  	IPSET_OPT_SIZE,
> +	IPSET_OPT_FORCEADD,
>  	/* Create-specific options, filled out by the kernel */
>  	IPSET_OPT_ELEMENTS,
>  	IPSET_OPT_REFERENCES,
> @@ -94,7 +95,8 @@ enum ipset_opt {
>  	| IPSET_FLAG(IPSET_OPT_RESIZE)	\
>  	| IPSET_FLAG(IPSET_OPT_SIZE)	\
>  	| IPSET_FLAG(IPSET_OPT_COUNTERS)\
> -	| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT))
> +	| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)\
> +	| IPSET_FLAG(IPSET_OPT_FORCEADD))
>  
>  #define IPSET_ADT_FLAGS			\
>  	(IPSET_FLAG(IPSET_OPT_IP)	\
> diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
> index c2bae85..0e1478e 100644
> --- a/include/libipset/linux_ip_set.h
> +++ b/include/libipset/linux_ip_set.h
> @@ -95,6 +95,7 @@ enum {
>  	IPSET_ATTR_PROBES,
>  	IPSET_ATTR_RESIZE,
>  	IPSET_ATTR_SIZE,
> +	IPSET_ATTR_FORCEADD,
>  	/* Kernel-only */
>  	IPSET_ATTR_ELEMENTS,
>  	IPSET_ATTR_REFERENCES,

Please use "make update_includes", in order to sync linux_ip_set.h.

> diff --git a/include/libipset/parse.h b/include/libipset/parse.h
> index 55981f2..93af5c3 100644
> --- a/include/libipset/parse.h
> +++ b/include/libipset/parse.h
> @@ -103,6 +103,9 @@ extern int ipset_parse_elem(struct ipset_session *session,
>  extern int ipset_call_parser(struct ipset_session *session,
>  			     const struct ipset_arg *arg,
>  			     const char *str);
> +extern int ipset_parse_forceadd(struct ipset_session *session,
> +                               enum ipset_opt opt, const char *str);
> +

This is not needed, see below.

>  /* Compatibility parser functions */
>  extern int ipset_parse_iptimeout(struct ipset_session *session,
> diff --git a/lib/data.c b/lib/data.c
> index 48ec98a..a841d7f 100644
> --- a/lib/data.c
> +++ b/lib/data.c
> @@ -57,6 +57,7 @@ struct ipset_data {
>  			uint32_t markmask;
>  			uint32_t gc;
>  			uint32_t size;
> +			uint8_t forceadd;

We can get rid of this too.

>  			/* Filled out by kernel */
>  			uint32_t references;
>  			uint32_t elements;
> @@ -309,6 +310,10 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
>  	case IPSET_OPT_CREATE_COMMENT:
>  		cadt_flag_type_attr(data, opt, IPSET_FLAG_WITH_COMMENT);
>  		break;
> +        case IPSET_OPT_FORCEADD:
> +		data->create.forceadd = *(const uint8_t *) value;
> +		break;
> +

Use cadt_flag_type_attr(), like just above in the 
IPSET_OPT_CREATE_COMMENT case.

>  	/* Create-specific options, filled out by the kernel */
>  	case IPSET_OPT_ELEMENTS:
>  		data->create.elements = *(const uint32_t *) value;
> @@ -481,6 +486,9 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
>  		return &data->create.resize;
>  	case IPSET_OPT_SIZE:
>  		return &data->create.size;
> +	case IPSET_OPT_FORCEADD:
> +		return &data->create.forceadd;
> +

Move the case to the bunch of the IPSET_OPT_CADT_FLAGS cases where just
cadt_flags is returned.

>  	/* Create-specific options, filled out by the kernel */
>  	case IPSET_OPT_ELEMENTS:
>  		return &data->create.elements;
> diff --git a/lib/debug.c b/lib/debug.c
> index 1240e22..14249e5 100644
> --- a/lib/debug.c
> +++ b/lib/debug.c
> @@ -46,6 +46,7 @@ static const struct ipset_attrname createattr2name[] = {
>  	[IPSET_ATTR_ELEMENTS]	= { .name = "ELEMENTS" },
>  	[IPSET_ATTR_REFERENCES]	= { .name = "REFERENCES" },
>  	[IPSET_ATTR_MEMSIZE]	= { .name = "MEMSIZE" },
> +	[IPSET_ATTR_FORCEADD]	= { .name = "FORCEADD" },
>  };

This can also be removed.

>  
>  static const struct ipset_attrname adtattr2name[] = {
> diff --git a/lib/ipset_hash_ip.c b/lib/ipset_hash_ip.c
> index 45185ec..cde0781 100644
> --- a/lib/ipset_hash_ip.c
> +++ b/lib/ipset_hash_ip.c
> @@ -383,10 +383,133 @@ static struct ipset_type ipset_hash_ip2 = {
>  	.description = "comment support",
>  };
>  
> +/* Parse commandline arguments */
> +static const struct ipset_arg hash_ip_create_args3[] = {
> +	{ .name = { "family", NULL },
> +	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_FAMILY,
> +	  .parse = ipset_parse_family,		.print = ipset_print_family,
> +	},
> +	/* Alias: family inet */
> +	{ .name = { "-4", NULL },
> +	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
> +	  .parse = ipset_parse_family,
> +	},
> +	/* Alias: family inet6 */
> +	{ .name = { "-6", NULL },
> +	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_FAMILY,
> +	  .parse = ipset_parse_family,
> +	},
> +	{ .name = { "hashsize", NULL },
> +	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_HASHSIZE,
> +	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
> +	},
> +	{ .name = { "maxelem", NULL },
> +	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_MAXELEM,
> +	  .parse = ipset_parse_uint32,		.print = ipset_print_number,
> +	},
> +	{ .name = { "netmask", NULL },
> +	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_NETMASK,
> +	  .parse = ipset_parse_netmask,		.print = ipset_print_number,
> +	},
> +	{ .name = { "timeout", NULL },
> +	  .has_arg = IPSET_MANDATORY_ARG,	.opt = IPSET_OPT_TIMEOUT,
> +	  .parse = ipset_parse_timeout,		.print = ipset_print_number,
> +	},
> +	{ .name = { "counters", NULL },
> +	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_COUNTERS,
> +	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
> +	},
> +	{ .name = { "comment", NULL },
> +	  .has_arg = IPSET_NO_ARG,		.opt = IPSET_OPT_CREATE_COMMENT,
> +	  .parse = ipset_parse_flag,		.print = ipset_print_flag,
> +	},
> +	{ .name = { "forceadd", NULL },
> +	  .has_arg = IPSET_NO_ARG,              .opt = IPSET_OPT_FORCEADD,
> +	  .parse = ipset_parse_forceadd,        .print = ipset_print_number,
> +        },

Please use ipset_parse_flag and ipset_print_flag here and in all the new 
revisions of the hash types. Also, adjust LIBVERSION in Make_global.am 
because of the new flag.

[...]

> diff --git a/lib/parse.c b/lib/parse.c
> index f1c1f0e..1953e9e 100644
> --- a/lib/parse.c
> +++ b/lib/parse.c
> @@ -1988,3 +1988,24 @@ out:
>  	free(saved);
>  	return ret;
>  }
> +
> +/**
> + * ipset_parse_forceadd - parse forceadd set option
> + * @session: session structure
> + * @opt: option kind of the data
> + * @str: string to parse
> + *
> + * Parse forceadd arg
> + * The value is stored in the data blob of the session.
> + *
> + * Returns 0 on success or a negative error code.
> + */
> +int
> +ipset_parse_forceadd(struct ipset_session *session,
> +		enum ipset_opt opt, const char *str)
> +{
> +	uint8_t force = 1;
> +        assert(session);
> +
> +        return ipset_session_data_set(session, opt, &force);
> +}

The parser function can be removed.

> diff --git a/lib/print.c b/lib/print.c
> index f81c074..86dfa03 100644
> --- a/lib/print.c
> +++ b/lib/print.c
> @@ -903,6 +903,10 @@ ipset_print_data(char *buf, unsigned int len,
>  	case IPSET_OPT_SIZE:
>  		size = ipset_print_number(buf, len, data, opt, env);
>  		break;
> +	case IPSET_OPT_FORCEADD:
> +		size = snprintf(buf, len, "forceadd", NULL);
> +		break;
> +
>  	default:
>  		return -1;
>  	}

And this isn't needed either.

> diff --git a/lib/session.c b/lib/session.c
> index d2957a5..095cbec 100644
> --- a/lib/session.c
> +++ b/lib/session.c
> @@ -413,6 +413,10 @@ static const struct ipset_attr_policy create_attrs[] = {
>  		.type = MNL_TYPE_U32,
>  		.opt = IPSET_OPT_MEMSIZE,
>  	},
> +	[IPSET_ATTR_FORCEADD] = {
> +		.type = MNL_TYPE_U8,
> +		.opt = IPSET_OPT_FORCEADD,
> +	},
>  };

This can also be deleted.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] ipset: add forceadd userspace support for hash set types
  2014-02-14 10:10   ` Jozsef Kadlecsik
@ 2014-02-14 10:24     ` Jozsef Kadlecsik
  0 siblings, 0 replies; 8+ messages in thread
From: Jozsef Kadlecsik @ 2014-02-14 10:24 UTC (permalink / raw)
  To: Josh Hunt; +Cc: netfilter-devel

Hi Josh,

One bit is missing and I forgot to add: the new option in userspace should 
be added to ipset_data_sizeof too, to the flags section.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] ipset: forceadd support
  2014-02-01 13:30 [PATCH 0/2] ipset: forceadd support Josh Hunt
  2014-02-01 13:30 ` [PATCH 1/2] ipset: add forceadd kernel support for hash set types Josh Hunt
  2014-02-01 13:30 ` [PATCH 2/2] ipset: add forceadd userspace " Josh Hunt
@ 2014-02-15 15:07 ` Bourne Without
  2014-02-15 15:09 ` Bourne Without
  3 siblings, 0 replies; 8+ messages in thread
From: Bourne Without @ 2014-02-15 15:07 UTC (permalink / raw)
  To: Josh Hunt, kadlec; +Cc: netfilter-devel

On 01.02.2014 14:30, Josh Hunt wrote:
> Forceadd is a property for hash set types and is passed in duration creation.
> When sets with this property enabled are full each subsequent 'add' operation
> will attempt to evict a random entry from the set. The implementation tries to
> keep the overhead low by checking to see which bucket the new entry hashes to.
> If that bucket has any entries it will evict the first one and add the new
> entry.
>
> Ex usage:
> ipset create foo hash:ip forceadd
>
> The intended usecase is for things like 'ban' lists where you may not be concerned
> with possibly evicting something early, and more concerned with the size of
> the hash itself.

For all who want to save time typing while testing, I've already 
implemented the forceadd support in the ipset-bash_completion code 
available here:


As it's set to enable the feature from ipset v6.21+, you need to either 
bump the ipset version, or just change line 886 in my code from:

  if ((ips_version[0] == 6 && ips_version[1] >= 21)); then

to:

  if ((ips_version[0] == 6 && ips_version[1] >= 20)); then

happy testing :)




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] ipset: forceadd support
  2014-02-01 13:30 [PATCH 0/2] ipset: forceadd support Josh Hunt
                   ` (2 preceding siblings ...)
  2014-02-15 15:07 ` [PATCH 0/2] ipset: forceadd support Bourne Without
@ 2014-02-15 15:09 ` Bourne Without
  3 siblings, 0 replies; 8+ messages in thread
From: Bourne Without @ 2014-02-15 15:09 UTC (permalink / raw)
  To: Josh Hunt; +Cc: netfilter-devel, kadlec

On 01.02.2014 14:30, Josh Hunt wrote:
> Forceadd is a property for hash set types and is passed in duration creation.
> When sets with this property enabled are full each subsequent 'add' operation
> will attempt to evict a random entry from the set. The implementation tries to
> keep the overhead low by checking to see which bucket the new entry hashes to.
> If that bucket has any entries it will evict the first one and add the new
> entry.
>
> Ex usage:
> ipset create foo hash:ip forceadd
>
> The intended usecase is for things like 'ban' lists where you may not be concerned
> with possibly evicting something early, and more concerned with the size of
> the hash itself.

Sorry forgot the url :/

For all who want to save time typing while testing, I've already 
implemented the forceadd support in the ipset-bash_completion code 
available here:

https://github.com/AllKind/ipset-bash-completion/tree/dev

As it's set to enable the feature from ipset v6.21+, you need to either 
bump the ipset version, or just change line 886 in my code from:

  if ((ips_version[0] == 6 && ips_version[1] >= 21)); then

to:

  if ((ips_version[0] == 6 && ips_version[1] >= 20)); then

happy testing :)

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2014-02-15 15:07 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-01 13:30 [PATCH 0/2] ipset: forceadd support Josh Hunt
2014-02-01 13:30 ` [PATCH 1/2] ipset: add forceadd kernel support for hash set types Josh Hunt
2014-02-14 10:06   ` Jozsef Kadlecsik
2014-02-01 13:30 ` [PATCH 2/2] ipset: add forceadd userspace " Josh Hunt
2014-02-14 10:10   ` Jozsef Kadlecsik
2014-02-14 10:24     ` Jozsef Kadlecsik
2014-02-15 15:07 ` [PATCH 0/2] ipset: forceadd support Bourne Without
2014-02-15 15:09 ` Bourne Without

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.