netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/18] netfilter updates for net-next (try 2)
@ 2013-04-29 18:22 Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 01/18] netfilter: ipset: Make possible to test elements marked with nomatch Pablo Neira Ayuso
                   ` (18 more replies)
  0 siblings, 19 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

Hi David,

This is a second pull request resolving the compilation issue in
net/sched/em_ipset.

The following patchset contains relevant updates for the Netfilter
tree, they are:

* Enhancements for ipset: Add the counter extension for sets, this
  information can be used from the iptables set match, to change
  the matching behaviour. Jozsef required to add the extension
  infrastructure and moved the existing timeout support upon it.
  This also includes a change in net/sched/em_ipset to adapt it to
  the new extension structure.

* Enhancements for performance boosting in nfnetlink_queue: Add new
  configuration flags that allows user-space to receive big packets (GRO)
  and to disable checksumming calculation. This were proposed by Eric
  Dumazet during the Netfilter Workshop 2013 in Copenhagen. Florian
  Westphal was kind enough to find the time to materialize the proposal.

* A sparse fix from Simon, he noticed it in the SCTP NAT helper, the fix
  required a change in the interface of sctp_end_cksum.

The following changes since commit 37fe0660981d7a1577409226f77554c2c5123e27:

  net: fix address check in rtnl_fdb_del (2013-04-25 04:14:08 -0400)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master

for you to fetch changes up to eee1d5a14780b9391ec51f3feaf4cffb521ddbb1:

  sctp: Correct type and usage of sctp_end_cksum() (2013-04-29 20:09:08 +0200)

----------------------------------------------------------------
Florian Westphal (4):
      netfilter: nf_queue: move device refcount bump to extra function
      netfilter: move skb_gso_segment into nfnetlink_queue module
      netfilter: nfnetlink_queue: add skb info attribute
      netfilter: nfnetlink_queue: avoid expensive gso segmentation and checksum fixup

Jozsef Kadlecsik (13):
      netfilter: ipset: Make possible to test elements marked with nomatch
      netfilter: ipset: Move often used IPv6 address masking function to header file
      netfilter: ipset: Introduce extensions to elements in the core
      netfilter: ipset: Unified bitmap type generation
      netfilter: ipset: Bitmap types using the unified code base
      netfilter: ipset: Unified hash type generation
      netfilter: ipset: Hash types using the unified code base
      netfilter: ipset: list:set type using the extension interface
      netfilter: ipset: Introduce the counter extension in the core
      netfilter: ipset: The bitmap types with counter support
      netfilter: ipset: The hash types with counter support
      netfilter: ipset: The list:set type with counter support
      netfilter: ipset: set match: add support to match the counters

Simon Horman (1):
      sctp: Correct type and usage of sctp_end_cksum()

 include/linux/netfilter/ipset/ip_set.h         |  126 ++-
 include/linux/netfilter/ipset/ip_set_ahash.h   | 1241 ------------------------
 include/linux/netfilter/ipset/ip_set_bitmap.h  |    6 +
 include/linux/netfilter/ipset/ip_set_timeout.h |  102 +-
 include/linux/netfilter/ipset/pfxlen.h         |    9 +
 include/net/netfilter/nf_queue.h               |    6 +
 include/net/sctp/checksum.h                    |    2 +-
 include/uapi/linux/netfilter/ipset/ip_set.h    |   36 +-
 include/uapi/linux/netfilter/nfnetlink_queue.h |   10 +-
 include/uapi/linux/netfilter/xt_set.h          |    9 +
 net/netfilter/ipset/ip_set_bitmap_gen.h        |  277 ++++++
 net/netfilter/ipset/ip_set_bitmap_ip.c         |  411 +++-----
 net/netfilter/ipset/ip_set_bitmap_ipmac.c      |  624 +++++-------
 net/netfilter/ipset/ip_set_bitmap_port.c       |  414 +++-----
 net/netfilter/ipset/ip_set_core.c              |   36 +-
 net/netfilter/ipset/ip_set_hash_gen.h          | 1100 +++++++++++++++++++++
 net/netfilter/ipset/ip_set_hash_ip.c           |  344 ++-----
 net/netfilter/ipset/ip_set_hash_ipport.c       |  362 +++----
 net/netfilter/ipset/ip_set_hash_ipportip.c     |  368 +++----
 net/netfilter/ipset/ip_set_hash_ipportnet.c    |  469 ++++-----
 net/netfilter/ipset/ip_set_hash_net.c          |  402 +++-----
 net/netfilter/ipset/ip_set_hash_netiface.c     |  478 ++++-----
 net/netfilter/ipset/ip_set_hash_netport.c      |  456 +++------
 net/netfilter/ipset/ip_set_list_set.c          |  622 +++++++-----
 net/netfilter/nf_nat_proto_sctp.c              |    5 +-
 net/netfilter/nf_queue.c                       |  143 +--
 net/netfilter/nfnetlink_queue_core.c           |  173 +++-
 net/netfilter/xt_set.c                         |   94 +-
 net/sched/em_ipset.c                           |    2 +-
 29 files changed, 3688 insertions(+), 4639 deletions(-)
 delete mode 100644 include/linux/netfilter/ipset/ip_set_ahash.h
 create mode 100644 net/netfilter/ipset/ip_set_bitmap_gen.h
 create mode 100644 net/netfilter/ipset/ip_set_hash_gen.h

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

* [PATCH 01/18] netfilter: ipset: Make possible to test elements marked with nomatch
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 02/18] netfilter: ipset: Move often used IPv6 address masking function to header file Pablo Neira Ayuso
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set.h      |    8 ++++++++
 net/netfilter/ipset/ip_set_hash_ipportnet.c |   14 ++++++++------
 net/netfilter/ipset/ip_set_hash_net.c       |   14 ++++++++------
 net/netfilter/ipset/ip_set_hash_netiface.c  |   14 ++++++++------
 net/netfilter/ipset/ip_set_hash_netport.c   |   14 ++++++++------
 5 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 7958e84..9701871 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -200,6 +200,14 @@ ip_set_eexist(int ret, u32 flags)
 	return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST);
 }
 
+/* Match elements marked with nomatch */
+static inline bool
+ip_set_enomatch(int ret, u32 flags, enum ipset_adt adt)
+{
+	return adt == IPSET_TEST &&
+	       ret == -ENOTEMPTY && ((flags >> 16) & IPSET_FLAG_NOMATCH);
+}
+
 /* Check the NLA_F_NET_BYTEORDER flag */
 static inline bool
 ip_set_attr_netorder(struct nlattr *tb[], int type)
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 10a30b4..b4836c8 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -279,10 +279,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
@@ -292,7 +292,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		data.ip = htonl(ip);
 		data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr + 1));
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	ip_to = ip;
@@ -610,15 +611,16 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	port = ntohs(data.port);
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index d6a5915..6dbe0af 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -225,16 +225,17 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
 		data.ip = htonl(ip & ip_set_hostmask(data.cidr));
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	ip_to = ip;
@@ -466,15 +467,16 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	ret = adtfn(set, &data, timeout, flags);
 
-	return ip_set_eexist(ret, flags) ? 0 : ret;
+	return ip_set_enomatch(ret, flags, adt) ? 1 :
+	       ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
 /* Create hash:ip type of sets */
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index f2b0a3c..2481620 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -396,13 +396,14 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_PHYSDEV)
 			data.physdev = 1;
-		if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
-			flags |= (cadt_flags << 16);
+		if (cadt_flags & IPSET_FLAG_NOMATCH)
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 	if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
 		data.ip = htonl(ip & ip_set_hostmask(data.cidr));
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	if (tb[IPSET_ATTR_IP_TO]) {
@@ -704,13 +705,14 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_PHYSDEV)
 			data.physdev = 1;
-		if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH))
-			flags |= (cadt_flags << 16);
+		if (cadt_flags & IPSET_FLAG_NOMATCH)
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	ret = adtfn(set, &data, timeout, flags);
 
-	return ip_set_eexist(ret, flags) ? 0 : ret;
+	return ip_set_enomatch(ret, flags, adt) ? 1 :
+	       ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
 /* Create hash:ip type of sets */
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 349deb6..57b0550 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -272,16 +272,17 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
 		data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	port = port_to = ntohs(data.port);
@@ -561,15 +562,16 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
 
-	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
+	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
-			flags |= (cadt_flags << 16);
+			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
 		ret = adtfn(set, &data, timeout, flags);
-		return ip_set_eexist(ret, flags) ? 0 : ret;
+		return ip_set_enomatch(ret, flags, adt) ? 1 :
+		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
 	port = ntohs(data.port);
-- 
1.7.10.4

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

* [PATCH 02/18] netfilter: ipset: Move often used IPv6 address masking function to header file
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 01/18] netfilter: ipset: Make possible to test elements marked with nomatch Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 03/18] netfilter: ipset: Introduce extensions to elements in the core Pablo Neira Ayuso
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/pfxlen.h      |    9 +++++++++
 net/netfilter/ipset/ip_set_hash_ip.c        |    9 ---------
 net/netfilter/ipset/ip_set_hash_ipportnet.c |    9 ---------
 net/netfilter/ipset/ip_set_hash_net.c       |    9 ---------
 net/netfilter/ipset/ip_set_hash_netiface.c  |    9 ---------
 net/netfilter/ipset/ip_set_hash_netport.c   |    9 ---------
 6 files changed, 9 insertions(+), 45 deletions(-)

diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h
index 199fd11..1afbb94 100644
--- a/include/linux/netfilter/ipset/pfxlen.h
+++ b/include/linux/netfilter/ipset/pfxlen.h
@@ -41,4 +41,13 @@ do {						\
 	to = from | ~ip_set_hostmask(cidr);	\
 } while (0)
 
+static inline void
+ip6_netmask(union nf_inet_addr *ip, u8 prefix)
+{
+	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
+	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
+	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
+	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
+}
+
 #endif /*_PFXLEN_H */
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index b7d4cb4..c486ad2 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -255,15 +255,6 @@ hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
 	ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
 }
 
-static inline void
-ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
-	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
-	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
-	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
-}
-
 static bool
 hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
 {
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index b4836c8..352f8b4 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -446,15 +446,6 @@ hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
 }
 
 static inline void
-ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
-	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
-	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
-	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
-}
-
-static inline void
 hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr)
 {
 	ip6_netmask(&elem->ip2, cidr);
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 6dbe0af..370947c 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -343,15 +343,6 @@ hash_net6_data_zero_out(struct hash_net6_elem *elem)
 }
 
 static inline void
-ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
-	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
-	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
-	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
-}
-
-static inline void
 hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr)
 {
 	ip6_netmask(&elem->ip, cidr);
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index 2481620..dd9843e 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -529,15 +529,6 @@ hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
 }
 
 static inline void
-ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
-	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
-	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
-	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
-}
-
-static inline void
 hash_netiface6_data_netmask(struct hash_netiface6_elem *elem, u8 cidr)
 {
 	ip6_netmask(&elem->ip, cidr);
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 57b0550..cd1d3c1 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -407,15 +407,6 @@ hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
 }
 
 static inline void
-ip6_netmask(union nf_inet_addr *ip, u8 prefix)
-{
-	ip->ip6[0] &= ip_set_netmask6(prefix)[0];
-	ip->ip6[1] &= ip_set_netmask6(prefix)[1];
-	ip->ip6[2] &= ip_set_netmask6(prefix)[2];
-	ip->ip6[3] &= ip_set_netmask6(prefix)[3];
-}
-
-static inline void
 hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr)
 {
 	ip6_netmask(&elem->ip, cidr);
-- 
1.7.10.4


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

* [PATCH 03/18] netfilter: ipset: Introduce extensions to elements in the core
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 01/18] netfilter: ipset: Make possible to test elements marked with nomatch Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 02/18] netfilter: ipset: Move often used IPv6 address masking function to header file Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 04/18] netfilter: ipset: Unified bitmap type generation Pablo Neira Ayuso
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Introduce extensions to elements in the core and prepare timeout as
the first one.

This patch also modifies the em_ipset classifier to use the new
extension struct layout.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set.h         |   46 +++++++++--
 include/linux/netfilter/ipset/ip_set_timeout.h |  102 +++++-------------------
 net/netfilter/ipset/ip_set_core.c              |   24 ++++--
 net/netfilter/xt_set.c                         |   24 ++----
 net/sched/em_ipset.c                           |    2 +-
 5 files changed, 87 insertions(+), 111 deletions(-)

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 9701871..bf0220c 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -1,7 +1,7 @@
 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
  *                         Patrick Schaaf <bof@bof.de>
  *                         Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -47,10 +47,30 @@ enum ip_set_feature {
 	IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
 };
 
+/* Set extensions */
+enum ip_set_extension {
+	IPSET_EXT_NONE = 0,
+	IPSET_EXT_BIT_TIMEOUT = 1,
+	IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
+};
+
+/* Extension offsets */
+enum ip_set_offset {
+	IPSET_OFFSET_TIMEOUT = 0,
+	IPSET_OFFSET_MAX,
+};
+
+#define SET_WITH_TIMEOUT(s)	((s)->extensions & IPSET_EXT_TIMEOUT)
+
+struct ip_set_ext {
+	unsigned long timeout;
+};
+
 struct ip_set;
 
 typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
-			   u32 timeout, u32 flags);
+			   const struct ip_set_ext *ext,
+			   struct ip_set_ext *mext, u32 flags);
 
 /* Kernel API function options */
 struct ip_set_adt_opt {
@@ -58,7 +78,7 @@ struct ip_set_adt_opt {
 	u8 dim;			/* Dimension of match/target */
 	u8 flags;		/* Direction and negation flags */
 	u32 cmdflags;		/* Command-like flags */
-	u32 timeout;		/* Timeout value */
+	struct ip_set_ext ext;	/* Extensions */
 };
 
 /* Set type, variant-specific part */
@@ -69,7 +89,7 @@ struct ip_set_type_variant {
 	 *			positive for matching element */
 	int (*kadt)(struct ip_set *set, const struct sk_buff *skb,
 		    const struct xt_action_param *par,
-		    enum ipset_adt adt, const struct ip_set_adt_opt *opt);
+		    enum ipset_adt adt, struct ip_set_adt_opt *opt);
 
 	/* Userspace: test/add/del entries
 	 *		returns negative error code,
@@ -151,6 +171,8 @@ struct ip_set {
 	u8 family;
 	/* The type revision */
 	u8 revision;
+	/* Extensions */
+	u8 extensions;
 	/* The type specific data */
 	void *data;
 };
@@ -167,19 +189,21 @@ extern void ip_set_nfnl_put(ip_set_id_t index);
 
 extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb,
 		      const struct xt_action_param *par,
-		      const struct ip_set_adt_opt *opt);
+		      struct ip_set_adt_opt *opt);
 extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb,
 		      const struct xt_action_param *par,
-		      const struct ip_set_adt_opt *opt);
+		      struct ip_set_adt_opt *opt);
 extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb,
 		       const struct xt_action_param *par,
-		       const struct ip_set_adt_opt *opt);
+		       struct ip_set_adt_opt *opt);
 
 /* Utility functions */
 extern void *ip_set_alloc(size_t size);
 extern void ip_set_free(void *members);
 extern int ip_set_get_ipaddr4(struct nlattr *nla,  __be32 *ipaddr);
 extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr);
+extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
+				 struct ip_set_ext *ext);
 
 static inline int
 ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr)
@@ -292,4 +316,12 @@ bitmap_bytes(u32 a, u32 b)
 	return 4 * ((((b - a + 8) / 8) + 3) / 4);
 }
 
+#include <linux/netfilter/ipset/ip_set_timeout.h>
+
+#define IP_SET_INIT_KEXT(skb, opt, map)		\
+	{ .timeout = ip_set_adt_opt_timeout(opt, map) }
+
+#define IP_SET_INIT_UEXT(map)			\
+	{ .timeout = (map)->timeout }
+
 #endif /*_IP_SET_H */
diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h
index 41d9cfa..3aac041 100644
--- a/include/linux/netfilter/ipset/ip_set_timeout.h
+++ b/include/linux/netfilter/ipset/ip_set_timeout.h
@@ -1,7 +1,7 @@
 #ifndef _IP_SET_TIMEOUT_H
 #define _IP_SET_TIMEOUT_H
 
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -17,13 +17,14 @@
 #define IPSET_GC_PERIOD(timeout) \
 	((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1)
 
-/* Set is defined without timeout support: timeout value may be 0 */
-#define IPSET_NO_TIMEOUT	UINT_MAX
+/* Entry is set with no timeout value */
+#define IPSET_ELEM_PERMANENT	0
 
-#define with_timeout(timeout)	((timeout) != IPSET_NO_TIMEOUT)
+/* Set is defined with timeout support: timeout value may be 0 */
+#define IPSET_NO_TIMEOUT	UINT_MAX
 
-#define opt_timeout(opt, map)	\
-	(with_timeout((opt)->timeout) ? (opt)->timeout : (map)->timeout)
+#define ip_set_adt_opt_timeout(opt, map)	\
+((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout)
 
 static inline unsigned int
 ip_set_timeout_uget(struct nlattr *tb)
@@ -38,61 +39,6 @@ ip_set_timeout_uget(struct nlattr *tb)
 	return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout;
 }
 
-#ifdef IP_SET_BITMAP_TIMEOUT
-
-/* Bitmap specific timeout constants and macros for the entries */
-
-/* Bitmap entry is unset */
-#define IPSET_ELEM_UNSET	0
-/* Bitmap entry is set with no timeout value */
-#define IPSET_ELEM_PERMANENT	(UINT_MAX/2)
-
-static inline bool
-ip_set_timeout_test(unsigned long timeout)
-{
-	return timeout != IPSET_ELEM_UNSET &&
-	       (timeout == IPSET_ELEM_PERMANENT ||
-		time_is_after_jiffies(timeout));
-}
-
-static inline bool
-ip_set_timeout_expired(unsigned long timeout)
-{
-	return timeout != IPSET_ELEM_UNSET &&
-	       timeout != IPSET_ELEM_PERMANENT &&
-	       time_is_before_jiffies(timeout);
-}
-
-static inline unsigned long
-ip_set_timeout_set(u32 timeout)
-{
-	unsigned long t;
-
-	if (!timeout)
-		return IPSET_ELEM_PERMANENT;
-
-	t = msecs_to_jiffies(timeout * 1000) + jiffies;
-	if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT)
-		/* Bingo! */
-		t++;
-
-	return t;
-}
-
-static inline u32
-ip_set_timeout_get(unsigned long timeout)
-{
-	return timeout == IPSET_ELEM_PERMANENT ? 0 :
-		jiffies_to_msecs(timeout - jiffies)/1000;
-}
-
-#else
-
-/* Hash specific timeout constants and macros for the entries */
-
-/* Hash entry is set with no timeout value */
-#define IPSET_ELEM_PERMANENT	0
-
 static inline bool
 ip_set_timeout_test(unsigned long timeout)
 {
@@ -101,36 +47,32 @@ ip_set_timeout_test(unsigned long timeout)
 }
 
 static inline bool
-ip_set_timeout_expired(unsigned long timeout)
+ip_set_timeout_expired(unsigned long *timeout)
 {
-	return timeout != IPSET_ELEM_PERMANENT &&
-	       time_is_before_jiffies(timeout);
+	return *timeout != IPSET_ELEM_PERMANENT &&
+	       time_is_before_jiffies(*timeout);
 }
 
-static inline unsigned long
-ip_set_timeout_set(u32 timeout)
+static inline void
+ip_set_timeout_set(unsigned long *timeout, u32 t)
 {
-	unsigned long t;
-
-	if (!timeout)
-		return IPSET_ELEM_PERMANENT;
+	if (!t) {
+		*timeout = IPSET_ELEM_PERMANENT;
+		return;
+	}
 
-	t = msecs_to_jiffies(timeout * 1000) + jiffies;
-	if (t == IPSET_ELEM_PERMANENT)
+	*timeout = msecs_to_jiffies(t * 1000) + jiffies;
+	if (*timeout == IPSET_ELEM_PERMANENT)
 		/* Bingo! :-) */
-		t++;
-
-	return t;
+		(*timeout)--;
 }
 
 static inline u32
-ip_set_timeout_get(unsigned long timeout)
+ip_set_timeout_get(unsigned long *timeout)
 {
-	return timeout == IPSET_ELEM_PERMANENT ? 0 :
-		jiffies_to_msecs(timeout - jiffies)/1000;
+	return *timeout == IPSET_ELEM_PERMANENT ? 0 :
+		jiffies_to_msecs(*timeout - jiffies)/1000;
 }
-#endif /* ! IP_SET_BITMAP_TIMEOUT */
 
 #endif	/* __KERNEL__ */
-
 #endif /* _IP_SET_TIMEOUT_H */
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 86f5e26..4486285d 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
  *                         Patrick Schaaf <bof@bof.de>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -315,6 +315,19 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr)
 }
 EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
 
+int
+ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
+		      struct ip_set_ext *ext)
+{
+	if (tb[IPSET_ATTR_TIMEOUT]) {
+		if (!(set->extensions & IPSET_EXT_TIMEOUT))
+			return -IPSET_ERR_TIMEOUT;
+		ext->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ip_set_get_extensions);
+
 /*
  * Creating/destroying/renaming/swapping affect the existence and
  * the properties of a set. All of these can be executed from userspace
@@ -365,8 +378,7 @@ ip_set_rcu_get(ip_set_id_t index)
 
 int
 ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
-	    const struct xt_action_param *par,
-	    const struct ip_set_adt_opt *opt)
+	    const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
 	struct ip_set *set = ip_set_rcu_get(index);
 	int ret = 0;
@@ -404,8 +416,7 @@ EXPORT_SYMBOL_GPL(ip_set_test);
 
 int
 ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
-	   const struct xt_action_param *par,
-	   const struct ip_set_adt_opt *opt)
+	   const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
 	struct ip_set *set = ip_set_rcu_get(index);
 	int ret;
@@ -427,8 +438,7 @@ EXPORT_SYMBOL_GPL(ip_set_add);
 
 int
 ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
-	   const struct xt_action_param *par,
-	   const struct ip_set_adt_opt *opt)
+	   const struct xt_action_param *par, struct ip_set_adt_opt *opt)
 {
 	struct ip_set *set = ip_set_rcu_get(index);
 	int ret = 0;
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c
index 865a9e5..636c519 100644
--- a/net/netfilter/xt_set.c
+++ b/net/netfilter/xt_set.c
@@ -1,7 +1,7 @@
 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
  *                         Patrick Schaaf <bof@bof.de>
  *                         Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -30,7 +30,7 @@ MODULE_ALIAS("ip6t_SET");
 static inline int
 match_set(ip_set_id_t index, const struct sk_buff *skb,
 	  const struct xt_action_param *par,
-	  const struct ip_set_adt_opt *opt, int inv)
+	  struct ip_set_adt_opt *opt, int inv)
 {
 	if (ip_set_test(index, skb, par, opt))
 		inv = !inv;
@@ -38,20 +38,12 @@ match_set(ip_set_id_t index, const struct sk_buff *skb,
 }
 
 #define ADT_OPT(n, f, d, fs, cfs, t)	\
-const struct ip_set_adt_opt n = {	\
-	.family	= f,			\
-	.dim = d,			\
-	.flags = fs,			\
-	.cmdflags = cfs,		\
-	.timeout = t,			\
-}
-#define ADT_MOPT(n, f, d, fs, cfs, t)	\
 struct ip_set_adt_opt n = {		\
 	.family	= f,			\
 	.dim = d,			\
 	.flags = fs,			\
 	.cmdflags = cfs,		\
-	.timeout = t,			\
+	.ext.timeout = t,		\
 }
 
 /* Revision 0 interface: backward compatible with netfilter/iptables */
@@ -305,15 +297,15 @@ static unsigned int
 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 {
 	const struct xt_set_info_target_v2 *info = par->targinfo;
-	ADT_MOPT(add_opt, par->family, info->add_set.dim,
-		 info->add_set.flags, info->flags, info->timeout);
+	ADT_OPT(add_opt, par->family, info->add_set.dim,
+		info->add_set.flags, info->flags, info->timeout);
 	ADT_OPT(del_opt, par->family, info->del_set.dim,
 		info->del_set.flags, 0, UINT_MAX);
 
 	/* Normalize to fit into jiffies */
-	if (add_opt.timeout != IPSET_NO_TIMEOUT &&
-	    add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
-		add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
+	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
+	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
+		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
 	if (info->add_set.index != IPSET_INVALID_ID)
 		ip_set_add(info->add_set.index, skb, par, &add_opt);
 	if (info->del_set.index != IPSET_INVALID_ID)
diff --git a/net/sched/em_ipset.c b/net/sched/em_ipset.c
index 3130320..938b7cb 100644
--- a/net/sched/em_ipset.c
+++ b/net/sched/em_ipset.c
@@ -83,7 +83,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
 	opt.dim = set->dim;
 	opt.flags = set->flags;
 	opt.cmdflags = 0;
-	opt.timeout = ~0u;
+	opt.ext.timeout = ~0u;
 
 	network_offset = skb_network_offset(skb);
 	skb_pull(skb, network_offset);
-- 
1.7.10.4


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

* [PATCH 04/18] netfilter: ipset: Unified bitmap type generation
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 03/18] netfilter: ipset: Introduce extensions to elements in the core Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-30  8:49   ` David Laight
  2013-04-29 18:22 ` [PATCH 05/18] netfilter: ipset: Bitmap types using the unified code base Pablo Neira Ayuso
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set_bitmap.h |    6 +
 net/netfilter/ipset/ip_set_bitmap_gen.h       |  265 +++++++++++++++++++++++++
 2 files changed, 271 insertions(+)
 create mode 100644 net/netfilter/ipset/ip_set_bitmap_gen.h

diff --git a/include/linux/netfilter/ipset/ip_set_bitmap.h b/include/linux/netfilter/ipset/ip_set_bitmap.h
index 1a30646..5e4662a 100644
--- a/include/linux/netfilter/ipset/ip_set_bitmap.h
+++ b/include/linux/netfilter/ipset/ip_set_bitmap.h
@@ -5,6 +5,12 @@
 
 #define IPSET_BITMAP_MAX_RANGE	0x0000FFFF
 
+enum {
+	IPSET_ADD_FAILED = 1,
+	IPSET_ADD_STORE_PLAIN_TIMEOUT,
+	IPSET_ADD_START_STORED_TIMEOUT,
+};
+
 /* Common functions */
 
 static inline u32
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
new file mode 100644
index 0000000..b993159
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -0,0 +1,265 @@
+/* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __IP_SET_BITMAP_IP_GEN_H
+#define __IP_SET_BITMAP_IP_GEN_H
+
+#define CONCAT(a, b)		a##b
+#define TOKEN(a,b)		CONCAT(a, b)
+
+#define mtype_do_test		TOKEN(MTYPE, _do_test)
+#define mtype_gc_test		TOKEN(MTYPE, _gc_test)
+#define mtype_is_filled		TOKEN(MTYPE, _is_filled)
+#define mtype_do_add		TOKEN(MTYPE, _do_add)
+#define mtype_do_del		TOKEN(MTYPE, _do_del)
+#define mtype_do_list		TOKEN(MTYPE, _do_list)
+#define mtype_do_head		TOKEN(MTYPE, _do_head)
+#define mtype_adt_elem		TOKEN(MTYPE, _adt_elem)
+#define mtype_add_timeout	TOKEN(MTYPE, _add_timeout)
+#define mtype_gc_init		TOKEN(MTYPE, _gc_init)
+#define mtype_kadt		TOKEN(MTYPE, _kadt)
+#define mtype_uadt		TOKEN(MTYPE, _uadt)
+#define mtype_destroy		TOKEN(MTYPE, _destroy)
+#define mtype_flush		TOKEN(MTYPE, _flush)
+#define mtype_head		TOKEN(MTYPE, _head)
+#define mtype_same_set		TOKEN(MTYPE, _same_set)
+#define mtype_elem		TOKEN(MTYPE, _elem)
+#define mtype_test		TOKEN(MTYPE, _test)
+#define mtype_add		TOKEN(MTYPE, _add)
+#define mtype_del		TOKEN(MTYPE, _del)
+#define mtype_list		TOKEN(MTYPE, _list)
+#define mtype_gc		TOKEN(MTYPE, _gc)
+#define mtype			MTYPE
+
+#define ext_timeout(e, m)	\
+	(unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
+#define get_ext(map, id)	((map)->extensions + (map)->dsize * (id))
+
+static void
+mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
+{
+	struct mtype *map = set->data;
+
+	init_timer(&map->gc);
+	map->gc.data = (unsigned long) set;
+	map->gc.function = gc;
+	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+	add_timer(&map->gc);
+}
+
+static void
+mtype_destroy(struct ip_set *set)
+{
+	struct mtype *map = set->data;
+
+	if (SET_WITH_TIMEOUT(set))
+		del_timer_sync(&map->gc);
+
+	ip_set_free(map->members);
+	if (map->dsize)
+		ip_set_free(map->extensions);
+	kfree(map);
+
+	set->data = NULL;
+}
+
+static void
+mtype_flush(struct ip_set *set)
+{
+	struct mtype *map = set->data;
+
+	memset(map->members, 0, map->memsize);
+}
+
+static int
+mtype_head(struct ip_set *set, struct sk_buff *skb)
+{
+	const struct mtype *map = set->data;
+	struct nlattr *nested;
+
+	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+	if (!nested)
+		goto nla_put_failure;
+	if (mtype_do_head(skb, map) ||
+	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
+	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
+			  htonl(sizeof(*map) +
+				map->memsize +
+				map->dsize * map->elements)) ||
+	    (SET_WITH_TIMEOUT(set) &&
+	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
+		goto nla_put_failure;
+	ipset_nest_end(skb, nested);
+
+	return 0;
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int
+mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	   struct ip_set_ext *mext, u32 flags)
+{
+	struct mtype *map = set->data;
+	const struct mtype_adt_elem *e = value;
+	void *x = get_ext(map, e->id);
+	int ret = mtype_do_test(e, map);
+
+	if (ret <= 0)
+		return ret;
+	if (SET_WITH_TIMEOUT(set) &&
+	    ip_set_timeout_expired(ext_timeout(x, map)))
+		return 0;
+	return 1;
+}
+
+static int
+mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	  struct ip_set_ext *mext, u32 flags)
+{
+	struct mtype *map = set->data;
+	const struct mtype_adt_elem *e = value;
+	void *x = get_ext(map, e->id);
+	int ret = mtype_do_add(e, map, flags);
+
+	if (ret == IPSET_ADD_FAILED) {
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(x, map)))
+			ret = 0;
+		else if (!(flags & IPSET_FLAG_EXIST))
+			return -IPSET_ERR_EXIST;
+	}
+
+	if (SET_WITH_TIMEOUT(set))
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+		mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
+#else
+		ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
+#endif
+
+	return 0;
+}
+
+static int
+mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	  struct ip_set_ext *mext, u32 flags)
+{
+	struct mtype *map = set->data;
+	const struct mtype_adt_elem *e = value;
+	const void *x = get_ext(map, e->id);
+
+	if (mtype_do_del(e, map) ||
+	    (SET_WITH_TIMEOUT(set) &&
+	     ip_set_timeout_expired(ext_timeout(x, map))))
+		return -IPSET_ERR_EXIST;
+
+	return 0;
+}
+
+static int
+mtype_list(const struct ip_set *set,
+	   struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct mtype *map = set->data;
+	struct nlattr *adt, *nested;
+	void *x;
+	u32 id, first = cb->args[2];
+
+	adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
+	if (!adt)
+		return -EMSGSIZE;
+	for (; cb->args[2] < map->elements; cb->args[2]++) {
+		id = cb->args[2];
+		x = get_ext(map, id);
+		if (!test_bit(id, map->members) ||
+		    (SET_WITH_TIMEOUT(set) &&
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+		     mtype_is_filled((const struct mtype_elem *) x) &&
+#endif
+		     ip_set_timeout_expired(ext_timeout(x, map))))
+			continue;
+		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+		if (!nested) {
+			if (id == first) {
+				nla_nest_cancel(skb, adt);
+				return -EMSGSIZE;
+			} else
+				goto nla_put_failure;
+		}
+		if (mtype_do_list(skb, map, id))
+			goto nla_put_failure;
+		if (SET_WITH_TIMEOUT(set)) {
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+					  htonl(ip_set_timeout_stored(map, id,
+							ext_timeout(x, map)))))
+				goto nla_put_failure;
+#else
+			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+					  htonl(ip_set_timeout_get(
+							ext_timeout(x, map)))))
+				goto nla_put_failure;
+#endif
+		}
+		ipset_nest_end(skb, nested);
+	}
+	ipset_nest_end(skb, adt);
+
+	/* Set listing finished */
+	cb->args[2] = 0;
+
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, nested);
+	ipset_nest_end(skb, adt);
+	if (unlikely(id == first)) {
+		cb->args[2] = 0;
+		return -EMSGSIZE;
+	}
+	return 0;
+}
+
+static void
+mtype_gc(unsigned long ul_set)
+{
+	struct ip_set *set = (struct ip_set *) ul_set;
+	struct mtype *map = set->data;
+	const void *x;
+	u32 id;
+
+	/* We run parallel with other readers (test element)
+	 * but adding/deleting new entries is locked out */
+	read_lock_bh(&set->lock);
+	for (id = 0; id < map->elements; id++)
+		if (mtype_gc_test(id, map)) {
+			x = get_ext(map, id);
+			if (ip_set_timeout_expired(ext_timeout(x, map)))
+				clear_bit(id, map->members);
+		}
+	read_unlock_bh(&set->lock);
+
+	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+	add_timer(&map->gc);
+}
+
+static const struct ip_set_type_variant mtype = {
+	.kadt	= mtype_kadt,
+	.uadt	= mtype_uadt,
+	.adt	= {
+		[IPSET_ADD] = mtype_add,
+		[IPSET_DEL] = mtype_del,
+		[IPSET_TEST] = mtype_test,
+	},
+	.destroy = mtype_destroy,
+	.flush	= mtype_flush,
+	.head	= mtype_head,
+	.list	= mtype_list,
+	.same_set = mtype_same_set,
+};
+
+#endif /* __IP_SET_BITMAP_IP_GEN_H */
-- 
1.7.10.4

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

* [PATCH 05/18] netfilter: ipset: Bitmap types using the unified code base
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (3 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 04/18] netfilter: ipset: Unified bitmap type generation Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 06/18] netfilter: ipset: Unified hash type generation Pablo Neira Ayuso
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_bitmap_ip.c    |  351 ++++--------------
 net/netfilter/ipset/ip_set_bitmap_ipmac.c |  562 +++++++++--------------------
 net/netfilter/ipset/ip_set_bitmap_port.c  |  361 ++++--------------
 3 files changed, 316 insertions(+), 958 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c
index 4a92fd4..f2ab011 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ip.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ip.c
@@ -1,6 +1,6 @@
 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
  *                         Patrick Schaaf <bof@bof.de>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -24,8 +24,6 @@
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
-#define IP_SET_BITMAP_TIMEOUT
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 
 #define REVISION_MIN	0
 #define REVISION_MAX	0
@@ -35,20 +33,28 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip");
 
+#define MTYPE		bitmap_ip
+
 /* Type structure */
 struct bitmap_ip {
 	void *members;		/* the set members */
+	void *extensions;	/* data extensions */
 	u32 first_ip;		/* host byte order, included in range */
 	u32 last_ip;		/* host byte order, included in range */
 	u32 elements;		/* number of max elements in the set */
 	u32 hosts;		/* number of hosts in a subnet */
 	size_t memsize;		/* members size */
+	size_t dsize;		/* extensions struct size */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
 	u8 netmask;		/* subnet netmask */
 	u32 timeout;		/* timeout parameter */
 	struct timer_list gc;	/* garbage collection */
 };
 
-/* Base variant */
+/* ADT structure for generic function args */
+struct bitmap_ip_adt_elem {
+	u16 id;
+};
 
 static inline u32
 ip_to_id(const struct bitmap_ip *m, u32 ip)
@@ -56,188 +62,67 @@ ip_to_id(const struct bitmap_ip *m, u32 ip)
 	return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts;
 }
 
-static int
-bitmap_ip_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	const struct bitmap_ip *map = set->data;
-	u16 id = *(u16 *)value;
-
-	return !!test_bit(id, map->members);
-}
-
-static int
-bitmap_ip_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct bitmap_ip *map = set->data;
-	u16 id = *(u16 *)value;
-
-	if (test_and_set_bit(id, map->members))
-		return -IPSET_ERR_EXIST;
-
-	return 0;
-}
+/* Common functions */
 
-static int
-bitmap_ip_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
 {
-	struct bitmap_ip *map = set->data;
-	u16 id = *(u16 *)value;
-
-	if (!test_and_clear_bit(id, map->members))
-		return -IPSET_ERR_EXIST;
-
-	return 0;
+	return !!test_bit(e->id, map->members);
 }
 
-static int
-bitmap_ip_list(const struct ip_set *set,
-	       struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map)
 {
-	const struct bitmap_ip *map = set->data;
-	struct nlattr *atd, *nested;
-	u32 id, first = cb->args[2];
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	for (; cb->args[2] < map->elements; cb->args[2]++) {
-		id = cb->args[2];
-		if (!test_bit(id, map->members))
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, atd);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
-				    htonl(map->first_ip + id * map->hosts)))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
-	return 0;
-
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, atd);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+	return !!test_bit(id, map->members);
 }
 
-/* Timeout variant */
-
-static int
-bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map,
+		 u32 flags)
 {
-	const struct bitmap_ip *map = set->data;
-	const unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-
-	return ip_set_timeout_test(members[id]);
+	return !!test_and_set_bit(e->id, map->members);
 }
 
-static int
-bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map)
 {
-	struct bitmap_ip *map = set->data;
-	unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-
-	if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
-		return -IPSET_ERR_EXIST;
-
-	members[id] = ip_set_timeout_set(timeout);
-
-	return 0;
+	return !test_and_clear_bit(e->id, map->members);
 }
 
-static int
-bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id)
 {
-	struct bitmap_ip *map = set->data;
-	unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-	int ret = -IPSET_ERR_EXIST;
-
-	if (ip_set_timeout_test(members[id]))
-		ret = 0;
-
-	members[id] = IPSET_ELEM_UNSET;
-	return ret;
+	return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
+			htonl(map->first_ip + id * map->hosts));
 }
 
-static int
-bitmap_ip_tlist(const struct ip_set *set,
-		struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_ip_do_head(struct sk_buff *skb, const struct bitmap_ip *map)
 {
-	const struct bitmap_ip *map = set->data;
-	struct nlattr *adt, *nested;
-	u32 id, first = cb->args[2];
-	const unsigned long *members = map->members;
-
-	adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!adt)
-		return -EMSGSIZE;
-	for (; cb->args[2] < map->elements; cb->args[2]++) {
-		id = cb->args[2];
-		if (!ip_set_timeout_test(members[id]))
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, adt);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
-				    htonl(map->first_ip + id * map->hosts)) ||
-		    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-				  htonl(ip_set_timeout_get(members[id]))))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, adt);
-
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
-
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, adt);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+	return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
+	       nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
+	       (map->netmask != 32 &&
+		nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask));
 }
 
 static int
 bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb,
 	       const struct xt_action_param *par,
-	       enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	       enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
 	struct bitmap_ip *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
+	struct bitmap_ip_adt_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
 	u32 ip;
 
 	ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
 	if (ip < map->first_ip || ip > map->last_ip)
 		return -IPSET_ERR_BITMAP_RANGE;
 
-	ip = ip_to_id(map, ip);
+	e.id = ip_to_id(map, ip);
 
-	return adtfn(set, &ip, opt_timeout(opt, map), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
@@ -246,8 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 {
 	struct bitmap_ip *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	u32 timeout = map->timeout;
-	u32 ip, ip_to, id;
+	u32 ip, ip_to;
+	struct bitmap_ip_adt_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -257,22 +143,17 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (ip < map->first_ip || ip > map->last_ip)
 		return -IPSET_ERR_BITMAP_RANGE;
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(map->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
-
 	if (adt == IPSET_TEST) {
-		id = ip_to_id(map, ip);
-		return adtfn(set, &id, timeout, flags);
+		e.id = ip_to_id(map, ip);
+		return adtfn(set, &e, &ext, &ext, flags);
 	}
 
 	if (tb[IPSET_ATTR_IP_TO]) {
@@ -297,8 +178,8 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 		return -IPSET_ERR_BITMAP_RANGE;
 
 	for (; !before(ip_to, ip); ip += map->hosts) {
-		id = ip_to_id(map, ip);
-		ret = adtfn(set, &id, timeout, flags);
+		e.id = ip_to_id(map, ip);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -308,54 +189,6 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static void
-bitmap_ip_destroy(struct ip_set *set)
-{
-	struct bitmap_ip *map = set->data;
-
-	if (with_timeout(map->timeout))
-		del_timer_sync(&map->gc);
-
-	ip_set_free(map->members);
-	kfree(map);
-
-	set->data = NULL;
-}
-
-static void
-bitmap_ip_flush(struct ip_set *set)
-{
-	struct bitmap_ip *map = set->data;
-
-	memset(map->members, 0, map->memsize);
-}
-
-static int
-bitmap_ip_head(struct ip_set *set, struct sk_buff *skb)
-{
-	const struct bitmap_ip *map = set->data;
-	struct nlattr *nested;
-
-	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-	if (!nested)
-		goto nla_put_failure;
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
-	    nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
-	    (map->netmask != 32 &&
-	     nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask)) ||
-	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
-			  htonl(sizeof(*map) + map->memsize)) ||
-	    (with_timeout(map->timeout) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
-		goto nla_put_failure;
-	ipset_nest_end(skb, nested);
-
-	return 0;
-nla_put_failure:
-	return -EMSGSIZE;
-}
-
 static bool
 bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
 {
@@ -365,70 +198,22 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b)
 	return x->first_ip == y->first_ip &&
 	       x->last_ip == y->last_ip &&
 	       x->netmask == y->netmask &&
-	       x->timeout == y->timeout;
+	       x->timeout == y->timeout &&
+	       a->extensions == b->extensions;
 }
 
-static const struct ip_set_type_variant bitmap_ip = {
-	.kadt	= bitmap_ip_kadt,
-	.uadt	= bitmap_ip_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_ip_add,
-		[IPSET_DEL] = bitmap_ip_del,
-		[IPSET_TEST] = bitmap_ip_test,
-	},
-	.destroy = bitmap_ip_destroy,
-	.flush	= bitmap_ip_flush,
-	.head	= bitmap_ip_head,
-	.list	= bitmap_ip_list,
-	.same_set = bitmap_ip_same_set,
-};
+/* Plain variant */
 
-static const struct ip_set_type_variant bitmap_tip = {
-	.kadt	= bitmap_ip_kadt,
-	.uadt	= bitmap_ip_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_ip_tadd,
-		[IPSET_DEL] = bitmap_ip_tdel,
-		[IPSET_TEST] = bitmap_ip_ttest,
-	},
-	.destroy = bitmap_ip_destroy,
-	.flush	= bitmap_ip_flush,
-	.head	= bitmap_ip_head,
-	.list	= bitmap_ip_tlist,
-	.same_set = bitmap_ip_same_set,
+struct bitmap_ip_elem {
 };
 
-static void
-bitmap_ip_gc(unsigned long ul_set)
-{
-	struct ip_set *set = (struct ip_set *) ul_set;
-	struct bitmap_ip *map = set->data;
-	unsigned long *table = map->members;
-	u32 id;
-
-	/* We run parallel with other readers (test element)
-	 * but adding/deleting new entries is locked out */
-	read_lock_bh(&set->lock);
-	for (id = 0; id < map->elements; id++)
-		if (ip_set_timeout_expired(table[id]))
-			table[id] = IPSET_ELEM_UNSET;
-	read_unlock_bh(&set->lock);
-
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
+/* Timeout variant */
 
-static void
-bitmap_ip_gc_init(struct ip_set *set)
-{
-	struct bitmap_ip *map = set->data;
+struct bitmap_ipt_elem {
+	unsigned long timeout;
+};
 
-	init_timer(&map->gc);
-	map->gc.data = (unsigned long) set;
-	map->gc.function = bitmap_ip_gc;
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
+#include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
 
@@ -440,6 +225,13 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map,
 	map->members = ip_set_alloc(map->memsize);
 	if (!map->members)
 		return false;
+	if (map->dsize) {
+		map->extensions = ip_set_alloc(map->dsize * elements);
+		if (!map->extensions) {
+			kfree(map->members);
+			return false;
+		}
+	}
 	map->first_ip = first_ip;
 	map->last_ip = last_ip;
 	map->elements = elements;
@@ -526,8 +318,12 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	if (!map)
 		return -ENOMEM;
 
+	map->memsize = bitmap_bytes(0, elements - 1);
+	set->variant = &bitmap_ip;
 	if (tb[IPSET_ATTR_TIMEOUT]) {
-		map->memsize = elements * sizeof(unsigned long);
+		map->dsize = sizeof(struct bitmap_ipt_elem);
+		map->offset[IPSET_OFFSET_TIMEOUT] =
+			offsetof(struct bitmap_ipt_elem, timeout);
 
 		if (!init_map_ip(set, map, first_ip, last_ip,
 				 elements, hosts, netmask)) {
@@ -536,19 +332,16 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 		}
 
 		map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-		set->variant = &bitmap_tip;
+		set->extensions |= IPSET_EXT_TIMEOUT;
 
-		bitmap_ip_gc_init(set);
+		bitmap_ip_gc_init(set, bitmap_ip_gc);
 	} else {
-		map->memsize = bitmap_bytes(0, elements - 1);
-
+		map->dsize = 0;
 		if (!init_map_ip(set, map, first_ip, last_ip,
 				 elements, hosts, netmask)) {
 			kfree(map);
 			return -ENOMEM;
 		}
-
-		set->variant = &bitmap_ip;
 	}
 	return 0;
 }
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
index d7df6ac..960810d 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
@@ -1,7 +1,7 @@
 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
  *                         Patrick Schaaf <bof@bof.de>
  *			   Martin Josefsson <gandalf@wlug.westbo.se>
- * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -23,7 +23,6 @@
 
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
 #define REVISION_MIN	0
@@ -34,333 +33,198 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_bitmap:ip,mac");
 
+#define MTYPE		bitmap_ipmac
+#define IP_SET_BITMAP_STORED_TIMEOUT
+
 enum {
-	MAC_EMPTY,		/* element is not set */
-	MAC_FILLED,		/* element is set with MAC */
 	MAC_UNSET,		/* element is set, without MAC */
+	MAC_FILLED,		/* element is set with MAC */
 };
 
 /* Type structure */
 struct bitmap_ipmac {
 	void *members;		/* the set members */
+	void *extensions;	/* MAC + data extensions */
 	u32 first_ip;		/* host byte order, included in range */
 	u32 last_ip;		/* host byte order, included in range */
+	u32 elements;		/* number of max elements in the set */
 	u32 timeout;		/* timeout value */
 	struct timer_list gc;	/* garbage collector */
+	size_t memsize;		/* members size */
 	size_t dsize;		/* size of element */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
 };
 
 /* ADT structure for generic function args */
-struct ipmac {
-	u32 id;			/* id in array */
-	unsigned char *ether;	/* ethernet address */
+struct bitmap_ipmac_adt_elem {
+	u16 id;
+	unsigned char *ether;
 };
 
-/* Member element without and with timeout */
-
-struct ipmac_elem {
+struct bitmap_ipmac_elem {
 	unsigned char ether[ETH_ALEN];
-	unsigned char match;
+	unsigned char filled;
 } __attribute__ ((aligned));
 
-struct ipmac_telem {
-	unsigned char ether[ETH_ALEN];
-	unsigned char match;
-	unsigned long timeout;
-} __attribute__ ((aligned));
-
-static inline void *
-bitmap_ipmac_elem(const struct bitmap_ipmac *map, u32 id)
+static inline u32
+ip_to_id(const struct bitmap_ipmac *m, u32 ip)
 {
-	return (void *)((char *)map->members + id * map->dsize);
+	return ip - m->first_ip;
 }
 
-static inline bool
-bitmap_timeout(const struct bitmap_ipmac *map, u32 id)
+static inline struct bitmap_ipmac_elem *
+get_elem(void *extensions, u16 id, size_t dsize)
 {
-	const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
-
-	return ip_set_timeout_test(elem->timeout);
+	return (struct bitmap_ipmac_elem *)(extensions + id * dsize);
 }
 
-static inline bool
-bitmap_expired(const struct bitmap_ipmac *map, u32 id)
-{
-	const struct ipmac_telem *elem = bitmap_ipmac_elem(map, id);
-
-	return ip_set_timeout_expired(elem->timeout);
-}
+/* Common functions */
 
 static inline int
-bitmap_ipmac_exist(const struct ipmac_telem *elem)
-{
-	return elem->match == MAC_UNSET ||
-	       (elem->match == MAC_FILLED &&
-		!ip_set_timeout_expired(elem->timeout));
-}
-
-/* Base variant */
-
-static int
-bitmap_ipmac_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	const struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
-
-	switch (elem->match) {
-	case MAC_UNSET:
-		/* Trigger kernel to fill out the ethernet address */
-		return -EAGAIN;
-	case MAC_FILLED:
-		return data->ether == NULL ||
-		       ether_addr_equal(data->ether, elem->ether);
-	}
-	return 0;
-}
-
-static int
-bitmap_ipmac_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
-
-	switch (elem->match) {
-	case MAC_UNSET:
-		if (!data->ether)
-			/* Already added without ethernet address */
-			return -IPSET_ERR_EXIST;
-		/* Fill the MAC address */
-		memcpy(elem->ether, data->ether, ETH_ALEN);
-		elem->match = MAC_FILLED;
-		break;
-	case MAC_FILLED:
-		return -IPSET_ERR_EXIST;
-	case MAC_EMPTY:
-		if (data->ether) {
-			memcpy(elem->ether, data->ether, ETH_ALEN);
-			elem->match = MAC_FILLED;
-		} else
-			elem->match = MAC_UNSET;
-	}
-
-	return 0;
-}
-
-static int
-bitmap_ipmac_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
+bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e,
+		     const struct bitmap_ipmac *map)
 {
-	struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
-
-	if (elem->match == MAC_EMPTY)
-		return -IPSET_ERR_EXIST;
+	const struct bitmap_ipmac_elem *elem;
 
-	elem->match = MAC_EMPTY;
-
-	return 0;
+	if (!test_bit(e->id, map->members))
+		return 0;
+	elem = get_elem(map->extensions, e->id, map->dsize);
+	if (elem->filled == MAC_FILLED)
+		return e->ether == NULL ||
+		       ether_addr_equal(e->ether, elem->ether);
+	/* Trigger kernel to fill out the ethernet address */
+	return -EAGAIN;
 }
 
-static int
-bitmap_ipmac_list(const struct ip_set *set,
-		  struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map)
 {
-	const struct bitmap_ipmac *map = set->data;
-	const struct ipmac_elem *elem;
-	struct nlattr *atd, *nested;
-	u32 id, first = cb->args[2];
-	u32 last = map->last_ip - map->first_ip;
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	for (; cb->args[2] <= last; cb->args[2]++) {
-		id = cb->args[2];
-		elem = bitmap_ipmac_elem(map, id);
-		if (elem->match == MAC_EMPTY)
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, atd);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
-				    htonl(map->first_ip + id)) ||
-		    (elem->match == MAC_FILLED &&
-		     nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN,
-			     elem->ether)))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
+	const struct bitmap_ipmac_elem *elem;
 
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, atd);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+	if (!test_bit(id, map->members))
+		return 0;
+	elem = get_elem(map->extensions, id, map->dsize);
+	/* Timer not started for the incomplete elements */
+	return elem->filled == MAC_FILLED;
 }
 
-/* Timeout variant */
-
-static int
-bitmap_ipmac_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem)
 {
-	const struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	const struct ipmac_elem *elem = bitmap_ipmac_elem(map, data->id);
-
-	switch (elem->match) {
-	case MAC_UNSET:
-		/* Trigger kernel to fill out the ethernet address */
-		return -EAGAIN;
-	case MAC_FILLED:
-		return (data->ether == NULL ||
-			ether_addr_equal(data->ether, elem->ether)) &&
-		       !bitmap_expired(map, data->id);
-	}
-	return 0;
+	return elem->filled == MAC_FILLED;
 }
 
-static int
-bitmap_ipmac_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ipmac_add_timeout(unsigned long *timeout,
+			 const struct bitmap_ipmac_adt_elem *e,
+			 const struct ip_set_ext *ext,
+			 struct bitmap_ipmac *map, int mode)
 {
-	struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
-	bool flag_exist = flags & IPSET_FLAG_EXIST;
+	u32 t = ext->timeout;
 
-	switch (elem->match) {
-	case MAC_UNSET:
-		if (!(data->ether || flag_exist))
-			/* Already added without ethernet address */
-			return -IPSET_ERR_EXIST;
-		/* Fill the MAC address and activate the timer */
-		memcpy(elem->ether, data->ether, ETH_ALEN);
-		elem->match = MAC_FILLED;
-		if (timeout == map->timeout)
+	if (mode == IPSET_ADD_START_STORED_TIMEOUT) {
+		if (t == map->timeout)
 			/* Timeout was not specified, get stored one */
-			timeout = elem->timeout;
-		elem->timeout = ip_set_timeout_set(timeout);
-		break;
-	case MAC_FILLED:
-		if (!(bitmap_expired(map, data->id) || flag_exist))
-			return -IPSET_ERR_EXIST;
-		/* Fall through */
-	case MAC_EMPTY:
-		if (data->ether) {
-			memcpy(elem->ether, data->ether, ETH_ALEN);
-			elem->match = MAC_FILLED;
-		} else
-			elem->match = MAC_UNSET;
+			t = *timeout;
+		ip_set_timeout_set(timeout, t);
+	} else {
 		/* If MAC is unset yet, we store plain timeout value
 		 * because the timer is not activated yet
 		 * and we can reuse it later when MAC is filled out,
 		 * possibly by the kernel */
-		elem->timeout = data->ether ? ip_set_timeout_set(timeout)
-					    : timeout;
-		break;
+		if (e->ether)
+			ip_set_timeout_set(timeout, t);
+		else
+			*timeout = t;
 	}
-
 	return 0;
 }
 
-static int
-bitmap_ipmac_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e,
+		    struct bitmap_ipmac *map, u32 flags)
 {
-	struct bitmap_ipmac *map = set->data;
-	const struct ipmac *data = value;
-	struct ipmac_telem *elem = bitmap_ipmac_elem(map, data->id);
+	struct bitmap_ipmac_elem *elem;
+
+	elem = get_elem(map->extensions, e->id, map->dsize);
+	if (test_and_set_bit(e->id, map->members)) {
+		if (elem->filled == MAC_FILLED) {
+			if (e->ether && (flags & IPSET_FLAG_EXIST))
+				memcpy(elem->ether, e->ether, ETH_ALEN);
+			return IPSET_ADD_FAILED;
+		} else if (!e->ether)
+			/* Already added without ethernet address */
+			return IPSET_ADD_FAILED;
+		/* Fill the MAC address and trigger the timer activation */
+		memcpy(elem->ether, e->ether, ETH_ALEN);
+		elem->filled = MAC_FILLED;
+		return IPSET_ADD_START_STORED_TIMEOUT;
+	} else if (e->ether) {
+		/* We can store MAC too */
+		memcpy(elem->ether, e->ether, ETH_ALEN);
+		elem->filled = MAC_FILLED;
+		return 0;
+	} else {
+		elem->filled = MAC_UNSET;
+		/* MAC is not stored yet, don't start timer */
+		return IPSET_ADD_STORE_PLAIN_TIMEOUT;
+	}
+}
 
-	if (elem->match == MAC_EMPTY || bitmap_expired(map, data->id))
-		return -IPSET_ERR_EXIST;
+static inline int
+bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e,
+		    struct bitmap_ipmac *map)
+{
+	return !test_and_clear_bit(e->id, map->members);
+}
 
-	elem->match = MAC_EMPTY;
+static inline unsigned long
+ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout)
+{
+	const struct bitmap_ipmac_elem *elem =
+		get_elem(map->extensions, id, map->dsize);
 
-	return 0;
+	return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) :
+					    *timeout;
 }
 
-static int
-bitmap_ipmac_tlist(const struct ip_set *set,
-		   struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map,
+		     u32 id)
 {
-	const struct bitmap_ipmac *map = set->data;
-	const struct ipmac_telem *elem;
-	struct nlattr *atd, *nested;
-	u32 id, first = cb->args[2];
-	u32 timeout, last = map->last_ip - map->first_ip;
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	for (; cb->args[2] <= last; cb->args[2]++) {
-		id = cb->args[2];
-		elem = bitmap_ipmac_elem(map, id);
-		if (!bitmap_ipmac_exist(elem))
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, atd);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_ipaddr4(skb, IPSET_ATTR_IP,
-				    htonl(map->first_ip + id)) ||
-		    (elem->match == MAC_FILLED &&
-		     nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN,
-			     elem->ether)))
-			goto nla_put_failure;
-		timeout = elem->match == MAC_UNSET ? elem->timeout
-				: ip_set_timeout_get(elem->timeout);
-		if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout)))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
+	const struct bitmap_ipmac_elem *elem =
+		get_elem(map->extensions, id, map->dsize);
 
-	return 0;
+	return nla_put_ipaddr4(skb, IPSET_ATTR_IP,
+			       htonl(map->first_ip + id)) ||
+	       (elem->filled == MAC_FILLED &&
+		nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, elem->ether));
+}
 
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, atd);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+static inline int
+bitmap_ipmac_do_head(struct sk_buff *skb, const struct bitmap_ipmac *map)
+{
+	return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
+	       nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip));
 }
 
 static int
 bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
 		  const struct xt_action_param *par,
-		  enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
 	struct bitmap_ipmac *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct ipmac data;
+	struct bitmap_ipmac_adt_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+	u32 ip;
 
 	/* MAC can be src only */
 	if (!(opt->flags & IPSET_DIM_TWO_SRC))
 		return 0;
 
-	data.id = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
-	if (data.id < map->first_ip || data.id > map->last_ip)
+	ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
+	if (ip < map->first_ip || ip > map->last_ip)
 		return -IPSET_ERR_BITMAP_RANGE;
 
 	/* Backward compatibility: we don't check the second flag */
@@ -368,10 +232,10 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
 	    (skb_mac_header(skb) + ETH_HLEN) > skb->data)
 		return -EINVAL;
 
-	data.id -= map->first_ip;
-	data.ether = eth_hdr(skb)->h_source;
+	e.id = ip_to_id(map, ip);
+	e.ether = eth_hdr(skb)->h_source;
 
-	return adtfn(set, &data, opt_timeout(opt, map), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
@@ -380,8 +244,9 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
 {
 	const struct bitmap_ipmac *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct ipmac data;
-	u32 timeout = map->timeout;
+	struct bitmap_ipmac_adt_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
+	u32 ip;
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -391,80 +256,25 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &data.id);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
-	if (data.id < map->first_ip || data.id > map->last_ip)
+	if (ip < map->first_ip || ip > map->last_ip)
 		return -IPSET_ERR_BITMAP_RANGE;
 
+	e.id = ip_to_id(map, ip);
 	if (tb[IPSET_ATTR_ETHER])
-		data.ether = nla_data(tb[IPSET_ATTR_ETHER]);
+		e.ether = nla_data(tb[IPSET_ATTR_ETHER]);
 	else
-		data.ether = NULL;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(map->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+		e.ether = NULL;
 
-	data.id -= map->first_ip;
-
-	ret = adtfn(set, &data, timeout, flags);
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 	return ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
-static void
-bitmap_ipmac_destroy(struct ip_set *set)
-{
-	struct bitmap_ipmac *map = set->data;
-
-	if (with_timeout(map->timeout))
-		del_timer_sync(&map->gc);
-
-	ip_set_free(map->members);
-	kfree(map);
-
-	set->data = NULL;
-}
-
-static void
-bitmap_ipmac_flush(struct ip_set *set)
-{
-	struct bitmap_ipmac *map = set->data;
-
-	memset(map->members, 0,
-	       (map->last_ip - map->first_ip + 1) * map->dsize);
-}
-
-static int
-bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb)
-{
-	const struct bitmap_ipmac *map = set->data;
-	struct nlattr *nested;
-
-	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-	if (!nested)
-		goto nla_put_failure;
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) ||
-	    nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) ||
-	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
-			  htonl(sizeof(*map) +
-				((map->last_ip - map->first_ip + 1) *
-				 map->dsize))) ||
-	    (with_timeout(map->timeout) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
-		goto nla_put_failure;
-	ipset_nest_end(skb, nested);
-
-	return 0;
-nla_put_failure:
-	return -EMSGSIZE;
-}
-
 static bool
 bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
 {
@@ -473,85 +283,43 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b)
 
 	return x->first_ip == y->first_ip &&
 	       x->last_ip == y->last_ip &&
-	       x->timeout == y->timeout;
+	       x->timeout == y->timeout &&
+	       a->extensions == b->extensions;
 }
 
-static const struct ip_set_type_variant bitmap_ipmac = {
-	.kadt	= bitmap_ipmac_kadt,
-	.uadt	= bitmap_ipmac_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_ipmac_add,
-		[IPSET_DEL] = bitmap_ipmac_del,
-		[IPSET_TEST] = bitmap_ipmac_test,
-	},
-	.destroy = bitmap_ipmac_destroy,
-	.flush	= bitmap_ipmac_flush,
-	.head	= bitmap_ipmac_head,
-	.list	= bitmap_ipmac_list,
-	.same_set = bitmap_ipmac_same_set,
-};
-
-static const struct ip_set_type_variant bitmap_tipmac = {
-	.kadt	= bitmap_ipmac_kadt,
-	.uadt	= bitmap_ipmac_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_ipmac_tadd,
-		[IPSET_DEL] = bitmap_ipmac_tdel,
-		[IPSET_TEST] = bitmap_ipmac_ttest,
-	},
-	.destroy = bitmap_ipmac_destroy,
-	.flush	= bitmap_ipmac_flush,
-	.head	= bitmap_ipmac_head,
-	.list	= bitmap_ipmac_tlist,
-	.same_set = bitmap_ipmac_same_set,
-};
-
-static void
-bitmap_ipmac_gc(unsigned long ul_set)
-{
-	struct ip_set *set = (struct ip_set *) ul_set;
-	struct bitmap_ipmac *map = set->data;
-	struct ipmac_telem *elem;
-	u32 id, last = map->last_ip - map->first_ip;
-
-	/* We run parallel with other readers (test element)
-	 * but adding/deleting new entries is locked out */
-	read_lock_bh(&set->lock);
-	for (id = 0; id <= last; id++) {
-		elem = bitmap_ipmac_elem(map, id);
-		if (elem->match == MAC_FILLED &&
-		    ip_set_timeout_expired(elem->timeout))
-			elem->match = MAC_EMPTY;
-	}
-	read_unlock_bh(&set->lock);
+/* Plain variant */
 
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
+/* Timeout variant */
 
-static void
-bitmap_ipmac_gc_init(struct ip_set *set)
-{
-	struct bitmap_ipmac *map = set->data;
+struct bitmap_ipmact_elem {
+	struct {
+		unsigned char ether[ETH_ALEN];
+		unsigned char filled;
+	} __attribute__ ((aligned));
+	unsigned long timeout;
+};
 
-	init_timer(&map->gc);
-	map->gc.data = (unsigned long) set;
-	map->gc.function = bitmap_ipmac_gc;
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
+#include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip,mac type of sets */
 
 static bool
 init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
-	       u32 first_ip, u32 last_ip)
+	       u32 first_ip, u32 last_ip, u32 elements)
 {
 	map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize);
 	if (!map->members)
 		return false;
+	if (map->dsize) {
+		map->extensions = ip_set_alloc(map->dsize * elements);
+		if (!map->extensions) {
+			kfree(map->members);
+			return false;
+		}
+	}
 	map->first_ip = first_ip;
 	map->last_ip = last_ip;
+	map->elements = elements;
 	map->timeout = IPSET_NO_TIMEOUT;
 
 	set->data = map;
@@ -605,28 +373,28 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
 	if (!map)
 		return -ENOMEM;
 
+	map->memsize = bitmap_bytes(0, elements - 1);
+	set->variant = &bitmap_ipmac;
 	if (tb[IPSET_ATTR_TIMEOUT]) {
-		map->dsize = sizeof(struct ipmac_telem);
+		map->dsize = sizeof(struct bitmap_ipmact_elem);
+		map->offset[IPSET_OFFSET_TIMEOUT] =
+			offsetof(struct bitmap_ipmact_elem, timeout);
 
-		if (!init_map_ipmac(set, map, first_ip, last_ip)) {
+		if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
 			kfree(map);
 			return -ENOMEM;
 		}
-
 		map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = &bitmap_tipmac;
-
-		bitmap_ipmac_gc_init(set);
+		set->extensions |= IPSET_EXT_TIMEOUT;
+		bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
 	} else {
-		map->dsize = sizeof(struct ipmac_elem);
+		map->dsize = sizeof(struct bitmap_ipmac_elem);
 
-		if (!init_map_ipmac(set, map, first_ip, last_ip)) {
+		if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
 			kfree(map);
 			return -ENOMEM;
 		}
 		set->variant = &bitmap_ipmac;
-
 	}
 	return 0;
 }
diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c
index e6b2db7..27e2c57 100644
--- a/net/netfilter/ipset/ip_set_bitmap_port.c
+++ b/net/netfilter/ipset/ip_set_bitmap_port.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,8 +19,6 @@
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
-#define IP_SET_BITMAP_TIMEOUT
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 
 #define REVISION_MIN	0
 #define REVISION_MAX	0
@@ -30,194 +28,85 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_bitmap:port");
 
+#define MTYPE		bitmap_port
+
 /* Type structure */
 struct bitmap_port {
 	void *members;		/* the set members */
+	void *extensions;	/* data extensions */
 	u16 first_port;		/* host byte order, included in range */
 	u16 last_port;		/* host byte order, included in range */
+	u32 elements;		/* number of max elements in the set */
 	size_t memsize;		/* members size */
+	size_t dsize;		/* extensions struct size */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
 	u32 timeout;		/* timeout parameter */
 	struct timer_list gc;	/* garbage collection */
 };
 
-/* Base variant */
+/* ADT structure for generic function args */
+struct bitmap_port_adt_elem {
+	u16 id;
+};
 
-static int
-bitmap_port_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline u16
+port_to_id(const struct bitmap_port *m, u16 port)
 {
-	const struct bitmap_port *map = set->data;
-	u16 id = *(u16 *)value;
-
-	return !!test_bit(id, map->members);
+	return port - m->first_port;
 }
 
-static int
-bitmap_port_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct bitmap_port *map = set->data;
-	u16 id = *(u16 *)value;
-
-	if (test_and_set_bit(id, map->members))
-		return -IPSET_ERR_EXIST;
-
-	return 0;
-}
+/* Common functions */
 
-static int
-bitmap_port_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_port_do_test(const struct bitmap_port_adt_elem *e,
+		    const struct bitmap_port *map)
 {
-	struct bitmap_port *map = set->data;
-	u16 id = *(u16 *)value;
-
-	if (!test_and_clear_bit(id, map->members))
-		return -IPSET_ERR_EXIST;
-
-	return 0;
+	return !!test_bit(e->id, map->members);
 }
 
-static int
-bitmap_port_list(const struct ip_set *set,
-		 struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_port_gc_test(u16 id, const struct bitmap_port *map)
 {
-	const struct bitmap_port *map = set->data;
-	struct nlattr *atd, *nested;
-	u16 id, first = cb->args[2];
-	u16 last = map->last_port - map->first_port;
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	for (; cb->args[2] <= last; cb->args[2]++) {
-		id = cb->args[2];
-		if (!test_bit(id, map->members))
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, atd);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_net16(skb, IPSET_ATTR_PORT,
-				  htons(map->first_port + id)))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
-
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, atd);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+	return !!test_bit(id, map->members);
 }
 
-/* Timeout variant */
-
-static int
-bitmap_port_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_port_do_add(const struct bitmap_port_adt_elem *e,
+		   struct bitmap_port *map, u32 flags)
 {
-	const struct bitmap_port *map = set->data;
-	const unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-
-	return ip_set_timeout_test(members[id]);
+	return !!test_and_set_bit(e->id, map->members);
 }
 
-static int
-bitmap_port_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_port_do_del(const struct bitmap_port_adt_elem *e,
+		   struct bitmap_port *map)
 {
-	struct bitmap_port *map = set->data;
-	unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-
-	if (ip_set_timeout_test(members[id]) && !(flags & IPSET_FLAG_EXIST))
-		return -IPSET_ERR_EXIST;
-
-	members[id] = ip_set_timeout_set(timeout);
-
-	return 0;
+	return !test_and_clear_bit(e->id, map->members);
 }
 
-static int
-bitmap_port_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
+static inline int
+bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id)
 {
-	struct bitmap_port *map = set->data;
-	unsigned long *members = map->members;
-	u16 id = *(u16 *)value;
-	int ret = -IPSET_ERR_EXIST;
-
-	if (ip_set_timeout_test(members[id]))
-		ret = 0;
-
-	members[id] = IPSET_ELEM_UNSET;
-	return ret;
+	return nla_put_net16(skb, IPSET_ATTR_PORT,
+			     htons(map->first_port + id));
 }
 
-static int
-bitmap_port_tlist(const struct ip_set *set,
-		  struct sk_buff *skb, struct netlink_callback *cb)
+static inline int
+bitmap_port_do_head(struct sk_buff *skb, const struct bitmap_port *map)
 {
-	const struct bitmap_port *map = set->data;
-	struct nlattr *adt, *nested;
-	u16 id, first = cb->args[2];
-	u16 last = map->last_port - map->first_port;
-	const unsigned long *members = map->members;
-
-	adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!adt)
-		return -EMSGSIZE;
-	for (; cb->args[2] <= last; cb->args[2]++) {
-		id = cb->args[2];
-		if (!ip_set_timeout_test(members[id]))
-			continue;
-		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-		if (!nested) {
-			if (id == first) {
-				nla_nest_cancel(skb, adt);
-				return -EMSGSIZE;
-			} else
-				goto nla_put_failure;
-		}
-		if (nla_put_net16(skb, IPSET_ATTR_PORT,
-				  htons(map->first_port + id)) ||
-		    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-				  htonl(ip_set_timeout_get(members[id]))))
-			goto nla_put_failure;
-		ipset_nest_end(skb, nested);
-	}
-	ipset_nest_end(skb, adt);
-
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
-
-nla_put_failure:
-	nla_nest_cancel(skb, nested);
-	ipset_nest_end(skb, adt);
-	if (unlikely(id == first)) {
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
+	return nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port)) ||
+	       nla_put_net16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port));
 }
 
 static int
 bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
 		 const struct xt_action_param *par,
-		 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		 enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
 	struct bitmap_port *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
+	struct bitmap_port_adt_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
 	__be16 __port;
 	u16 port = 0;
 
@@ -230,9 +119,9 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb,
 	if (port < map->first_port || port > map->last_port)
 		return -IPSET_ERR_BITMAP_RANGE;
 
-	port -= map->first_port;
+	e.id = port_to_id(map, port);
 
-	return adtfn(set, &port, opt_timeout(opt, map), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
@@ -241,9 +130,10 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
 {
 	struct bitmap_port *map = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	u32 timeout = map->timeout;
+	struct bitmap_port_adt_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
 	u32 port;	/* wraparound */
-	u16 id, port_to;
+	u16 port_to;
 	int ret = 0;
 
 	if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
@@ -257,16 +147,13 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
 	port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
 	if (port < map->first_port || port > map->last_port)
 		return -IPSET_ERR_BITMAP_RANGE;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(map->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	ret = ip_set_get_extensions(set, tb, &ext);
+	if (ret)
+		return ret;
 
 	if (adt == IPSET_TEST) {
-		id = port - map->first_port;
-		return adtfn(set, &id, timeout, flags);
+		e.id = port_to_id(map, port);
+		return adtfn(set, &e, &ext, &ext, flags);
 	}
 
 	if (tb[IPSET_ATTR_PORT_TO]) {
@@ -283,8 +170,8 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
 		return -IPSET_ERR_BITMAP_RANGE;
 
 	for (; port <= port_to; port++) {
-		id = port - map->first_port;
-		ret = adtfn(set, &id, timeout, flags);
+		e.id = port_to_id(map, port);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -294,52 +181,6 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static void
-bitmap_port_destroy(struct ip_set *set)
-{
-	struct bitmap_port *map = set->data;
-
-	if (with_timeout(map->timeout))
-		del_timer_sync(&map->gc);
-
-	ip_set_free(map->members);
-	kfree(map);
-
-	set->data = NULL;
-}
-
-static void
-bitmap_port_flush(struct ip_set *set)
-{
-	struct bitmap_port *map = set->data;
-
-	memset(map->members, 0, map->memsize);
-}
-
-static int
-bitmap_port_head(struct ip_set *set, struct sk_buff *skb)
-{
-	const struct bitmap_port *map = set->data;
-	struct nlattr *nested;
-
-	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-	if (!nested)
-		goto nla_put_failure;
-	if (nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port)) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)) ||
-	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
-			  htonl(sizeof(*map) + map->memsize)) ||
-	    (with_timeout(map->timeout) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
-		goto nla_put_failure;
-	ipset_nest_end(skb, nested);
-
-	return 0;
-nla_put_failure:
-	return -EMSGSIZE;
-}
-
 static bool
 bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 {
@@ -348,71 +189,21 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b)
 
 	return x->first_port == y->first_port &&
 	       x->last_port == y->last_port &&
-	       x->timeout == y->timeout;
+	       x->timeout == y->timeout &&
+	       a->extensions == b->extensions;
 }
 
-static const struct ip_set_type_variant bitmap_port = {
-	.kadt	= bitmap_port_kadt,
-	.uadt	= bitmap_port_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_port_add,
-		[IPSET_DEL] = bitmap_port_del,
-		[IPSET_TEST] = bitmap_port_test,
-	},
-	.destroy = bitmap_port_destroy,
-	.flush	= bitmap_port_flush,
-	.head	= bitmap_port_head,
-	.list	= bitmap_port_list,
-	.same_set = bitmap_port_same_set,
-};
+/* Plain variant */
 
-static const struct ip_set_type_variant bitmap_tport = {
-	.kadt	= bitmap_port_kadt,
-	.uadt	= bitmap_port_uadt,
-	.adt	= {
-		[IPSET_ADD] = bitmap_port_tadd,
-		[IPSET_DEL] = bitmap_port_tdel,
-		[IPSET_TEST] = bitmap_port_ttest,
-	},
-	.destroy = bitmap_port_destroy,
-	.flush	= bitmap_port_flush,
-	.head	= bitmap_port_head,
-	.list	= bitmap_port_tlist,
-	.same_set = bitmap_port_same_set,
+struct bitmap_port_elem {
 };
 
-static void
-bitmap_port_gc(unsigned long ul_set)
-{
-	struct ip_set *set = (struct ip_set *) ul_set;
-	struct bitmap_port *map = set->data;
-	unsigned long *table = map->members;
-	u32 id;	/* wraparound */
-	u16 last = map->last_port - map->first_port;
-
-	/* We run parallel with other readers (test element)
-	 * but adding/deleting new entries is locked out */
-	read_lock_bh(&set->lock);
-	for (id = 0; id <= last; id++)
-		if (ip_set_timeout_expired(table[id]))
-			table[id] = IPSET_ELEM_UNSET;
-	read_unlock_bh(&set->lock);
-
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
-
-static void
-bitmap_port_gc_init(struct ip_set *set)
-{
-	struct bitmap_port *map = set->data;
+/* Timeout variant */
+struct bitmap_portt_elem {
+	unsigned long timeout;
+};
 
-	init_timer(&map->gc);
-	map->gc.data = (unsigned long) set;
-	map->gc.function = bitmap_port_gc;
-	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
-	add_timer(&map->gc);
-}
+#include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
 
@@ -423,6 +214,13 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
 	map->members = ip_set_alloc(map->memsize);
 	if (!map->members)
 		return false;
+	if (map->dsize) {
+		map->extensions = ip_set_alloc(map->dsize * map->elements);
+		if (!map->extensions) {
+			kfree(map->members);
+			return false;
+		}
+	}
 	map->first_port = first_port;
 	map->last_port = last_port;
 	map->timeout = IPSET_NO_TIMEOUT;
@@ -434,8 +232,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map,
 }
 
 static int
-bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
-		 u32 flags)
+bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
 	struct bitmap_port *map;
 	u16 first_port, last_port;
@@ -458,28 +255,28 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[],
 	if (!map)
 		return -ENOMEM;
 
+	map->elements = last_port - first_port + 1;
+	map->memsize = map->elements * sizeof(unsigned long);
+	set->variant = &bitmap_port;
 	if (tb[IPSET_ATTR_TIMEOUT]) {
-		map->memsize = (last_port - first_port + 1)
-			       * sizeof(unsigned long);
-
+		map->dsize = sizeof(struct bitmap_portt_elem);
+		map->offset[IPSET_OFFSET_TIMEOUT] =
+			offsetof(struct bitmap_portt_elem, timeout);
 		if (!init_map_port(set, map, first_port, last_port)) {
 			kfree(map);
 			return -ENOMEM;
 		}
 
 		map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-		set->variant = &bitmap_tport;
-
-		bitmap_port_gc_init(set);
+		set->extensions |= IPSET_EXT_TIMEOUT;
+		bitmap_port_gc_init(set, bitmap_port_gc);
 	} else {
-		map->memsize = bitmap_bytes(0, last_port - first_port);
-		pr_debug("memsize: %zu\n", map->memsize);
+		map->dsize = 0;
 		if (!init_map_port(set, map, first_port, last_port)) {
 			kfree(map);
 			return -ENOMEM;
 		}
 
-		set->variant = &bitmap_port;
 	}
 	return 0;
 }
-- 
1.7.10.4

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

* [PATCH 06/18] netfilter: ipset: Unified hash type generation
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (4 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 05/18] netfilter: ipset: Bitmap types using the unified code base Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 07/18] netfilter: ipset: Hash types using the unified code base Pablo Neira Ayuso
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set_ahash.h | 1241 --------------------------
 net/netfilter/ipset/ip_set_hash_gen.h        | 1039 +++++++++++++++++++++
 2 files changed, 1039 insertions(+), 1241 deletions(-)
 delete mode 100644 include/linux/netfilter/ipset/ip_set_ahash.h
 create mode 100644 net/netfilter/ipset/ip_set_hash_gen.h

diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h
deleted file mode 100644
index 0214c4c..0000000
--- a/include/linux/netfilter/ipset/ip_set_ahash.h
+++ /dev/null
@@ -1,1241 +0,0 @@
-#ifndef _IP_SET_AHASH_H
-#define _IP_SET_AHASH_H
-
-#include <linux/rcupdate.h>
-#include <linux/jhash.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
-
-#define CONCAT(a, b, c)		a##b##c
-#define TOKEN(a, b, c)		CONCAT(a, b, c)
-
-#define type_pf_next		TOKEN(TYPE, PF, _elem)
-
-/* Hashing which uses arrays to resolve clashing. The hash table is resized
- * (doubled) when searching becomes too long.
- * Internally jhash is used with the assumption that the size of the
- * stored data is a multiple of sizeof(u32). If storage supports timeout,
- * the timeout field must be the last one in the data structure - that field
- * is ignored when computing the hash key.
- *
- * Readers and resizing
- *
- * Resizing can be triggered by userspace command only, and those
- * are serialized by the nfnl mutex. During resizing the set is
- * read-locked, so the only possible concurrent operations are
- * the kernel side readers. Those must be protected by proper RCU locking.
- */
-
-/* Number of elements to store in an initial array block */
-#define AHASH_INIT_SIZE			4
-/* Max number of elements to store in an array block */
-#define AHASH_MAX_SIZE			(3*AHASH_INIT_SIZE)
-
-/* Max number of elements can be tuned */
-#ifdef IP_SET_HASH_WITH_MULTI
-#define AHASH_MAX(h)			((h)->ahash_max)
-
-static inline u8
-tune_ahash_max(u8 curr, u32 multi)
-{
-	u32 n;
-
-	if (multi < curr)
-		return curr;
-
-	n = curr + AHASH_INIT_SIZE;
-	/* Currently, at listing one hash bucket must fit into a message.
-	 * Therefore we have a hard limit here.
-	 */
-	return n > curr && n <= 64 ? n : curr;
-}
-#define TUNE_AHASH_MAX(h, multi)	\
-	((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi))
-#else
-#define AHASH_MAX(h)			AHASH_MAX_SIZE
-#define TUNE_AHASH_MAX(h, multi)
-#endif
-
-/* A hash bucket */
-struct hbucket {
-	void *value;		/* the array of the values */
-	u8 size;		/* size of the array */
-	u8 pos;			/* position of the first free entry */
-};
-
-/* The hash table: the table size stored here in order to make resizing easy */
-struct htable {
-	u8 htable_bits;		/* size of hash table == 2^htable_bits */
-	struct hbucket bucket[0]; /* hashtable buckets */
-};
-
-#define hbucket(h, i)		(&((h)->bucket[i]))
-
-/* Book-keeping of the prefixes added to the set */
-struct ip_set_hash_nets {
-	u8 cidr;		/* the different cidr values in the set */
-	u32 nets;		/* number of elements per cidr */
-};
-
-/* The generic ip_set hash structure */
-struct ip_set_hash {
-	struct htable *table;	/* the hash table */
-	u32 maxelem;		/* max elements in the hash */
-	u32 elements;		/* current element (vs timeout) */
-	u32 initval;		/* random jhash init value */
-	u32 timeout;		/* timeout value, if enabled */
-	struct timer_list gc;	/* garbage collection when timeout enabled */
-	struct type_pf_next next; /* temporary storage for uadd */
-#ifdef IP_SET_HASH_WITH_MULTI
-	u8 ahash_max;		/* max elements in an array block */
-#endif
-#ifdef IP_SET_HASH_WITH_NETMASK
-	u8 netmask;		/* netmask value for subnets to store */
-#endif
-#ifdef IP_SET_HASH_WITH_RBTREE
-	struct rb_root rbtree;
-#endif
-#ifdef IP_SET_HASH_WITH_NETS
-	struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */
-#endif
-};
-
-static size_t
-htable_size(u8 hbits)
-{
-	size_t hsize;
-
-	/* We must fit both into u32 in jhash and size_t */
-	if (hbits > 31)
-		return 0;
-	hsize = jhash_size(hbits);
-	if ((((size_t)-1) - sizeof(struct htable))/sizeof(struct hbucket)
-	    < hsize)
-		return 0;
-
-	return hsize * sizeof(struct hbucket) + sizeof(struct htable);
-}
-
-/* Compute htable_bits from the user input parameter hashsize */
-static u8
-htable_bits(u32 hashsize)
-{
-	/* Assume that hashsize == 2^htable_bits */
-	u8 bits = fls(hashsize - 1);
-	if (jhash_size(bits) != hashsize)
-		/* Round up to the first 2^n value */
-		bits = fls(hashsize);
-
-	return bits;
-}
-
-#ifdef IP_SET_HASH_WITH_NETS
-#ifdef IP_SET_HASH_WITH_NETS_PACKED
-/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
-#define CIDR(cidr)	(cidr + 1)
-#else
-#define CIDR(cidr)	(cidr)
-#endif
-
-#define SET_HOST_MASK(family)	(family == AF_INET ? 32 : 128)
-#ifdef IP_SET_HASH_WITH_MULTI
-#define NETS_LENGTH(family)	(SET_HOST_MASK(family) + 1)
-#else
-#define NETS_LENGTH(family)	SET_HOST_MASK(family)
-#endif
-
-/* Network cidr size book keeping when the hash stores different
- * sized networks */
-static void
-add_cidr(struct ip_set_hash *h, u8 cidr, u8 nets_length)
-{
-	int i, j;
-
-	/* Add in increasing prefix order, so larger cidr first */
-	for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) {
-		if (j != -1)
-			continue;
-		else if (h->nets[i].cidr < cidr)
-			j = i;
-		else if (h->nets[i].cidr == cidr) {
-			h->nets[i].nets++;
-			return;
-		}
-	}
-	if (j != -1) {
-		for (; i > j; i--) {
-			h->nets[i].cidr = h->nets[i - 1].cidr;
-			h->nets[i].nets = h->nets[i - 1].nets;
-		}
-	}
-	h->nets[i].cidr = cidr;
-	h->nets[i].nets = 1;
-}
-
-static void
-del_cidr(struct ip_set_hash *h, u8 cidr, u8 nets_length)
-{
-	u8 i, j;
-
-	for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++)
-		;
-	h->nets[i].nets--;
-
-	if (h->nets[i].nets != 0)
-		return;
-
-	for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) {
-		h->nets[j].cidr = h->nets[j + 1].cidr;
-		h->nets[j].nets = h->nets[j + 1].nets;
-	}
-}
-#else
-#define NETS_LENGTH(family)		0
-#endif
-
-/* Destroy the hashtable part of the set */
-static void
-ahash_destroy(struct htable *t)
-{
-	struct hbucket *n;
-	u32 i;
-
-	for (i = 0; i < jhash_size(t->htable_bits); i++) {
-		n = hbucket(t, i);
-		if (n->size)
-			/* FIXME: use slab cache */
-			kfree(n->value);
-	}
-
-	ip_set_free(t);
-}
-
-/* Calculate the actual memory size of the set data */
-static size_t
-ahash_memsize(const struct ip_set_hash *h, size_t dsize, u8 nets_length)
-{
-	u32 i;
-	struct htable *t = h->table;
-	size_t memsize = sizeof(*h)
-			 + sizeof(*t)
-#ifdef IP_SET_HASH_WITH_NETS
-			 + sizeof(struct ip_set_hash_nets) * nets_length
-#endif
-			 + jhash_size(t->htable_bits) * sizeof(struct hbucket);
-
-	for (i = 0; i < jhash_size(t->htable_bits); i++)
-			memsize += t->bucket[i].size * dsize;
-
-	return memsize;
-}
-
-/* Flush a hash type of set: destroy all elements */
-static void
-ip_set_hash_flush(struct ip_set *set)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	struct hbucket *n;
-	u32 i;
-
-	for (i = 0; i < jhash_size(t->htable_bits); i++) {
-		n = hbucket(t, i);
-		if (n->size) {
-			n->size = n->pos = 0;
-			/* FIXME: use slab cache */
-			kfree(n->value);
-		}
-	}
-#ifdef IP_SET_HASH_WITH_NETS
-	memset(h->nets, 0, sizeof(struct ip_set_hash_nets)
-			   * NETS_LENGTH(set->family));
-#endif
-	h->elements = 0;
-}
-
-/* Destroy a hash type of set */
-static void
-ip_set_hash_destroy(struct ip_set *set)
-{
-	struct ip_set_hash *h = set->data;
-
-	if (with_timeout(h->timeout))
-		del_timer_sync(&h->gc);
-
-	ahash_destroy(h->table);
-#ifdef IP_SET_HASH_WITH_RBTREE
-	rbtree_destroy(&h->rbtree);
-#endif
-	kfree(h);
-
-	set->data = NULL;
-}
-
-#endif /* _IP_SET_AHASH_H */
-
-#ifndef HKEY_DATALEN
-#define HKEY_DATALEN	sizeof(struct type_pf_elem)
-#endif
-
-#define HKEY(data, initval, htable_bits)			\
-(jhash2((u32 *)(data), HKEY_DATALEN/sizeof(u32), initval)	\
-	& jhash_mask(htable_bits))
-
-/* Type/family dependent function prototypes */
-
-#define type_pf_data_equal	TOKEN(TYPE, PF, _data_equal)
-#define type_pf_data_isnull	TOKEN(TYPE, PF, _data_isnull)
-#define type_pf_data_copy	TOKEN(TYPE, PF, _data_copy)
-#define type_pf_data_zero_out	TOKEN(TYPE, PF, _data_zero_out)
-#define type_pf_data_netmask	TOKEN(TYPE, PF, _data_netmask)
-#define type_pf_data_list	TOKEN(TYPE, PF, _data_list)
-#define type_pf_data_tlist	TOKEN(TYPE, PF, _data_tlist)
-#define type_pf_data_next	TOKEN(TYPE, PF, _data_next)
-#define type_pf_data_flags	TOKEN(TYPE, PF, _data_flags)
-#define type_pf_data_reset_flags TOKEN(TYPE, PF, _data_reset_flags)
-#ifdef IP_SET_HASH_WITH_NETS
-#define type_pf_data_match	TOKEN(TYPE, PF, _data_match)
-#else
-#define type_pf_data_match(d)	1
-#endif
-
-#define type_pf_elem		TOKEN(TYPE, PF, _elem)
-#define type_pf_telem		TOKEN(TYPE, PF, _telem)
-#define type_pf_data_timeout	TOKEN(TYPE, PF, _data_timeout)
-#define type_pf_data_expired	TOKEN(TYPE, PF, _data_expired)
-#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set)
-
-#define type_pf_elem_add	TOKEN(TYPE, PF, _elem_add)
-#define type_pf_add		TOKEN(TYPE, PF, _add)
-#define type_pf_del		TOKEN(TYPE, PF, _del)
-#define type_pf_test_cidrs	TOKEN(TYPE, PF, _test_cidrs)
-#define type_pf_test		TOKEN(TYPE, PF, _test)
-
-#define type_pf_elem_tadd	TOKEN(TYPE, PF, _elem_tadd)
-#define type_pf_del_telem	TOKEN(TYPE, PF, _ahash_del_telem)
-#define type_pf_expire		TOKEN(TYPE, PF, _expire)
-#define type_pf_tadd		TOKEN(TYPE, PF, _tadd)
-#define type_pf_tdel		TOKEN(TYPE, PF, _tdel)
-#define type_pf_ttest_cidrs	TOKEN(TYPE, PF, _ahash_ttest_cidrs)
-#define type_pf_ttest		TOKEN(TYPE, PF, _ahash_ttest)
-
-#define type_pf_resize		TOKEN(TYPE, PF, _resize)
-#define type_pf_tresize		TOKEN(TYPE, PF, _tresize)
-#define type_pf_flush		ip_set_hash_flush
-#define type_pf_destroy		ip_set_hash_destroy
-#define type_pf_head		TOKEN(TYPE, PF, _head)
-#define type_pf_list		TOKEN(TYPE, PF, _list)
-#define type_pf_tlist		TOKEN(TYPE, PF, _tlist)
-#define type_pf_same_set	TOKEN(TYPE, PF, _same_set)
-#define type_pf_kadt		TOKEN(TYPE, PF, _kadt)
-#define type_pf_uadt		TOKEN(TYPE, PF, _uadt)
-#define type_pf_gc		TOKEN(TYPE, PF, _gc)
-#define type_pf_gc_init		TOKEN(TYPE, PF, _gc_init)
-#define type_pf_variant		TOKEN(TYPE, PF, _variant)
-#define type_pf_tvariant	TOKEN(TYPE, PF, _tvariant)
-
-/* Flavour without timeout */
-
-/* Get the ith element from the array block n */
-#define ahash_data(n, i)	\
-	((struct type_pf_elem *)((n)->value) + (i))
-
-/* Add an element to the hash table when resizing the set:
- * we spare the maintenance of the internal counters. */
-static int
-type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value,
-		 u8 ahash_max, u32 cadt_flags)
-{
-	struct type_pf_elem *data;
-
-	if (n->pos >= n->size) {
-		void *tmp;
-
-		if (n->size >= ahash_max)
-			/* Trigger rehashing */
-			return -EAGAIN;
-
-		tmp = kzalloc((n->size + AHASH_INIT_SIZE)
-			      * sizeof(struct type_pf_elem),
-			      GFP_ATOMIC);
-		if (!tmp)
-			return -ENOMEM;
-		if (n->size) {
-			memcpy(tmp, n->value,
-			       sizeof(struct type_pf_elem) * n->size);
-			kfree(n->value);
-		}
-		n->value = tmp;
-		n->size += AHASH_INIT_SIZE;
-	}
-	data = ahash_data(n, n->pos++);
-	type_pf_data_copy(data, value);
-#ifdef IP_SET_HASH_WITH_NETS
-	/* Resizing won't overwrite stored flags */
-	if (cadt_flags)
-		type_pf_data_flags(data, cadt_flags);
-#endif
-	return 0;
-}
-
-/* Resize a hash: create a new hash table with doubling the hashsize
- * and inserting the elements to it. Repeat until we succeed or
- * fail due to memory pressures. */
-static int
-type_pf_resize(struct ip_set *set, bool retried)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t, *orig = h->table;
-	u8 htable_bits = orig->htable_bits;
-	struct type_pf_elem *data;
-	struct hbucket *n, *m;
-	u32 i, j, flags = 0;
-	int ret;
-
-retry:
-	ret = 0;
-	htable_bits++;
-	pr_debug("attempt to resize set %s from %u to %u, t %p\n",
-		 set->name, orig->htable_bits, htable_bits, orig);
-	if (!htable_bits) {
-		/* In case we have plenty of memory :-) */
-		pr_warning("Cannot increase the hashsize of set %s further\n",
-			   set->name);
-		return -IPSET_ERR_HASH_FULL;
-	}
-	t = ip_set_alloc(sizeof(*t)
-			 + jhash_size(htable_bits) * sizeof(struct hbucket));
-	if (!t)
-		return -ENOMEM;
-	t->htable_bits = htable_bits;
-
-	read_lock_bh(&set->lock);
-	for (i = 0; i < jhash_size(orig->htable_bits); i++) {
-		n = hbucket(orig, i);
-		for (j = 0; j < n->pos; j++) {
-			data = ahash_data(n, j);
-#ifdef IP_SET_HASH_WITH_NETS
-			flags = 0;
-			type_pf_data_reset_flags(data, &flags);
-#endif
-			m = hbucket(t, HKEY(data, h->initval, htable_bits));
-			ret = type_pf_elem_add(m, data, AHASH_MAX(h), flags);
-			if (ret < 0) {
-#ifdef IP_SET_HASH_WITH_NETS
-				type_pf_data_flags(data, flags);
-#endif
-				read_unlock_bh(&set->lock);
-				ahash_destroy(t);
-				if (ret == -EAGAIN)
-					goto retry;
-				return ret;
-			}
-		}
-	}
-
-	rcu_assign_pointer(h->table, t);
-	read_unlock_bh(&set->lock);
-
-	/* Give time to other readers of the set */
-	synchronize_rcu_bh();
-
-	pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
-		 orig->htable_bits, orig, t->htable_bits, t);
-	ahash_destroy(orig);
-
-	return 0;
-}
-
-static inline void
-type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
-
-/* Add an element to a hash and update the internal counters when succeeded,
- * otherwise report the proper error code. */
-static int
-type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t;
-	const struct type_pf_elem *d = value;
-	struct hbucket *n;
-	int i, ret = 0;
-	u32 key, multi = 0;
-	u32 cadt_flags = flags >> 16;
-
-	if (h->elements >= h->maxelem) {
-		if (net_ratelimit())
-			pr_warning("Set %s is full, maxelem %u reached\n",
-				   set->name, h->maxelem);
-		return -IPSET_ERR_HASH_FULL;
-	}
-
-	rcu_read_lock_bh();
-	t = rcu_dereference_bh(h->table);
-	key = HKEY(value, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++)
-		if (type_pf_data_equal(ahash_data(n, i), d, &multi)) {
-#ifdef IP_SET_HASH_WITH_NETS
-			if (flags & IPSET_FLAG_EXIST)
-				/* Support overwriting just the flags */
-				type_pf_data_flags(ahash_data(n, i),
-						   cadt_flags);
-#endif
-			ret = -IPSET_ERR_EXIST;
-			goto out;
-		}
-	TUNE_AHASH_MAX(h, multi);
-	ret = type_pf_elem_add(n, value, AHASH_MAX(h), cadt_flags);
-	if (ret != 0) {
-		if (ret == -EAGAIN)
-			type_pf_data_next(h, d);
-		goto out;
-	}
-
-#ifdef IP_SET_HASH_WITH_NETS
-	add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
-#endif
-	h->elements++;
-out:
-	rcu_read_unlock_bh();
-	return ret;
-}
-
-/* Delete an element from the hash: swap it with the last element
- * and free up space if possible.
- */
-static int
-type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	const struct type_pf_elem *d = value;
-	struct hbucket *n;
-	int i;
-	struct type_pf_elem *data;
-	u32 key, multi = 0;
-
-	key = HKEY(value, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++) {
-		data = ahash_data(n, i);
-		if (!type_pf_data_equal(data, d, &multi))
-			continue;
-		if (i != n->pos - 1)
-			/* Not last one */
-			type_pf_data_copy(data, ahash_data(n, n->pos - 1));
-
-		n->pos--;
-		h->elements--;
-#ifdef IP_SET_HASH_WITH_NETS
-		del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
-#endif
-		if (n->pos + AHASH_INIT_SIZE < n->size) {
-			void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
-					    * sizeof(struct type_pf_elem),
-					    GFP_ATOMIC);
-			if (!tmp)
-				return 0;
-			n->size -= AHASH_INIT_SIZE;
-			memcpy(tmp, n->value,
-			       n->size * sizeof(struct type_pf_elem));
-			kfree(n->value);
-			n->value = tmp;
-		}
-		return 0;
-	}
-
-	return -IPSET_ERR_EXIST;
-}
-
-#ifdef IP_SET_HASH_WITH_NETS
-
-/* Special test function which takes into account the different network
- * sizes added to the set */
-static int
-type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	struct hbucket *n;
-	const struct type_pf_elem *data;
-	int i, j = 0;
-	u32 key, multi = 0;
-	u8 nets_length = NETS_LENGTH(set->family);
-
-	pr_debug("test by nets\n");
-	for (; j < nets_length && h->nets[j].nets && !multi; j++) {
-		type_pf_data_netmask(d, h->nets[j].cidr);
-		key = HKEY(d, h->initval, t->htable_bits);
-		n = hbucket(t, key);
-		for (i = 0; i < n->pos; i++) {
-			data = ahash_data(n, i);
-			if (type_pf_data_equal(data, d, &multi))
-				return type_pf_data_match(data);
-		}
-	}
-	return 0;
-}
-#endif
-
-/* Test whether the element is added to the set */
-static int
-type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	struct type_pf_elem *d = value;
-	struct hbucket *n;
-	const struct type_pf_elem *data;
-	int i;
-	u32 key, multi = 0;
-
-#ifdef IP_SET_HASH_WITH_NETS
-	/* If we test an IP address and not a network address,
-	 * try all possible network sizes */
-	if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
-		return type_pf_test_cidrs(set, d, timeout);
-#endif
-
-	key = HKEY(d, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++) {
-		data = ahash_data(n, i);
-		if (type_pf_data_equal(data, d, &multi))
-			return type_pf_data_match(data);
-	}
-	return 0;
-}
-
-/* Reply a HEADER request: fill out the header part of the set */
-static int
-type_pf_head(struct ip_set *set, struct sk_buff *skb)
-{
-	const struct ip_set_hash *h = set->data;
-	struct nlattr *nested;
-	size_t memsize;
-
-	read_lock_bh(&set->lock);
-	memsize = ahash_memsize(h, with_timeout(h->timeout)
-					? sizeof(struct type_pf_telem)
-					: sizeof(struct type_pf_elem),
-				NETS_LENGTH(set->family));
-	read_unlock_bh(&set->lock);
-
-	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-	if (!nested)
-		goto nla_put_failure;
-	if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
-			  htonl(jhash_size(h->table->htable_bits))) ||
-	    nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
-		goto nla_put_failure;
-#ifdef IP_SET_HASH_WITH_NETMASK
-	if (h->netmask != HOST_MASK &&
-	    nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
-		goto nla_put_failure;
-#endif
-	if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
-	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
-	    (with_timeout(h->timeout) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))))
-		goto nla_put_failure;
-	ipset_nest_end(skb, nested);
-
-	return 0;
-nla_put_failure:
-	return -EMSGSIZE;
-}
-
-/* Reply a LIST/SAVE request: dump the elements of the specified set */
-static int
-type_pf_list(const struct ip_set *set,
-	     struct sk_buff *skb, struct netlink_callback *cb)
-{
-	const struct ip_set_hash *h = set->data;
-	const struct htable *t = h->table;
-	struct nlattr *atd, *nested;
-	const struct hbucket *n;
-	const struct type_pf_elem *data;
-	u32 first = cb->args[2];
-	/* We assume that one hash bucket fills into one page */
-	void *incomplete;
-	int i;
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	pr_debug("list hash set %s\n", set->name);
-	for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
-		incomplete = skb_tail_pointer(skb);
-		n = hbucket(t, cb->args[2]);
-		pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
-		for (i = 0; i < n->pos; i++) {
-			data = ahash_data(n, i);
-			pr_debug("list hash %lu hbucket %p i %u, data %p\n",
-				 cb->args[2], n, i, data);
-			nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-			if (!nested) {
-				if (cb->args[2] == first) {
-					nla_nest_cancel(skb, atd);
-					return -EMSGSIZE;
-				} else
-					goto nla_put_failure;
-			}
-			if (type_pf_data_list(skb, data))
-				goto nla_put_failure;
-			ipset_nest_end(skb, nested);
-		}
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
-
-nla_put_failure:
-	nlmsg_trim(skb, incomplete);
-	ipset_nest_end(skb, atd);
-	if (unlikely(first == cb->args[2])) {
-		pr_warning("Can't list set %s: one bucket does not fit into "
-			   "a message. Please report it!\n", set->name);
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
-}
-
-static int
-type_pf_kadt(struct ip_set *set, const struct sk_buff *skb,
-	     const struct xt_action_param *par,
-	     enum ipset_adt adt, const struct ip_set_adt_opt *opt);
-static int
-type_pf_uadt(struct ip_set *set, struct nlattr *tb[],
-	     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
-
-static const struct ip_set_type_variant type_pf_variant = {
-	.kadt	= type_pf_kadt,
-	.uadt	= type_pf_uadt,
-	.adt	= {
-		[IPSET_ADD] = type_pf_add,
-		[IPSET_DEL] = type_pf_del,
-		[IPSET_TEST] = type_pf_test,
-	},
-	.destroy = type_pf_destroy,
-	.flush	= type_pf_flush,
-	.head	= type_pf_head,
-	.list	= type_pf_list,
-	.resize	= type_pf_resize,
-	.same_set = type_pf_same_set,
-};
-
-/* Flavour with timeout support */
-
-#define ahash_tdata(n, i) \
-	(struct type_pf_elem *)((struct type_pf_telem *)((n)->value) + (i))
-
-static inline u32
-type_pf_data_timeout(const struct type_pf_elem *data)
-{
-	const struct type_pf_telem *tdata =
-		(const struct type_pf_telem *) data;
-
-	return tdata->timeout;
-}
-
-static inline bool
-type_pf_data_expired(const struct type_pf_elem *data)
-{
-	const struct type_pf_telem *tdata =
-		(const struct type_pf_telem *) data;
-
-	return ip_set_timeout_expired(tdata->timeout);
-}
-
-static inline void
-type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
-{
-	struct type_pf_telem *tdata = (struct type_pf_telem *) data;
-
-	tdata->timeout = ip_set_timeout_set(timeout);
-}
-
-static int
-type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value,
-		  u8 ahash_max, u32 cadt_flags, u32 timeout)
-{
-	struct type_pf_elem *data;
-
-	if (n->pos >= n->size) {
-		void *tmp;
-
-		if (n->size >= ahash_max)
-			/* Trigger rehashing */
-			return -EAGAIN;
-
-		tmp = kzalloc((n->size + AHASH_INIT_SIZE)
-			      * sizeof(struct type_pf_telem),
-			      GFP_ATOMIC);
-		if (!tmp)
-			return -ENOMEM;
-		if (n->size) {
-			memcpy(tmp, n->value,
-			       sizeof(struct type_pf_telem) * n->size);
-			kfree(n->value);
-		}
-		n->value = tmp;
-		n->size += AHASH_INIT_SIZE;
-	}
-	data = ahash_tdata(n, n->pos++);
-	type_pf_data_copy(data, value);
-	type_pf_data_timeout_set(data, timeout);
-#ifdef IP_SET_HASH_WITH_NETS
-	/* Resizing won't overwrite stored flags */
-	if (cadt_flags)
-		type_pf_data_flags(data, cadt_flags);
-#endif
-	return 0;
-}
-
-/* Delete expired elements from the hashtable */
-static void
-type_pf_expire(struct ip_set_hash *h, u8 nets_length)
-{
-	struct htable *t = h->table;
-	struct hbucket *n;
-	struct type_pf_elem *data;
-	u32 i;
-	int j;
-
-	for (i = 0; i < jhash_size(t->htable_bits); i++) {
-		n = hbucket(t, i);
-		for (j = 0; j < n->pos; j++) {
-			data = ahash_tdata(n, j);
-			if (type_pf_data_expired(data)) {
-				pr_debug("expired %u/%u\n", i, j);
-#ifdef IP_SET_HASH_WITH_NETS
-				del_cidr(h, CIDR(data->cidr), nets_length);
-#endif
-				if (j != n->pos - 1)
-					/* Not last one */
-					type_pf_data_copy(data,
-						ahash_tdata(n, n->pos - 1));
-				n->pos--;
-				h->elements--;
-			}
-		}
-		if (n->pos + AHASH_INIT_SIZE < n->size) {
-			void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
-					    * sizeof(struct type_pf_telem),
-					    GFP_ATOMIC);
-			if (!tmp)
-				/* Still try to delete expired elements */
-				continue;
-			n->size -= AHASH_INIT_SIZE;
-			memcpy(tmp, n->value,
-			       n->size * sizeof(struct type_pf_telem));
-			kfree(n->value);
-			n->value = tmp;
-		}
-	}
-}
-
-static int
-type_pf_tresize(struct ip_set *set, bool retried)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t, *orig = h->table;
-	u8 htable_bits = orig->htable_bits;
-	struct type_pf_elem *data;
-	struct hbucket *n, *m;
-	u32 i, j, flags = 0;
-	int ret;
-
-	/* Try to cleanup once */
-	if (!retried) {
-		i = h->elements;
-		write_lock_bh(&set->lock);
-		type_pf_expire(set->data, NETS_LENGTH(set->family));
-		write_unlock_bh(&set->lock);
-		if (h->elements <  i)
-			return 0;
-	}
-
-retry:
-	ret = 0;
-	htable_bits++;
-	pr_debug("attempt to resize set %s from %u to %u, t %p\n",
-		 set->name, orig->htable_bits, htable_bits, orig);
-	if (!htable_bits) {
-		/* In case we have plenty of memory :-) */
-		pr_warning("Cannot increase the hashsize of set %s further\n",
-			   set->name);
-		return -IPSET_ERR_HASH_FULL;
-	}
-	t = ip_set_alloc(sizeof(*t)
-			 + jhash_size(htable_bits) * sizeof(struct hbucket));
-	if (!t)
-		return -ENOMEM;
-	t->htable_bits = htable_bits;
-
-	read_lock_bh(&set->lock);
-	for (i = 0; i < jhash_size(orig->htable_bits); i++) {
-		n = hbucket(orig, i);
-		for (j = 0; j < n->pos; j++) {
-			data = ahash_tdata(n, j);
-#ifdef IP_SET_HASH_WITH_NETS
-			flags = 0;
-			type_pf_data_reset_flags(data, &flags);
-#endif
-			m = hbucket(t, HKEY(data, h->initval, htable_bits));
-			ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), flags,
-				ip_set_timeout_get(type_pf_data_timeout(data)));
-			if (ret < 0) {
-#ifdef IP_SET_HASH_WITH_NETS
-				type_pf_data_flags(data, flags);
-#endif
-				read_unlock_bh(&set->lock);
-				ahash_destroy(t);
-				if (ret == -EAGAIN)
-					goto retry;
-				return ret;
-			}
-		}
-	}
-
-	rcu_assign_pointer(h->table, t);
-	read_unlock_bh(&set->lock);
-
-	/* Give time to other readers of the set */
-	synchronize_rcu_bh();
-
-	ahash_destroy(orig);
-
-	return 0;
-}
-
-static int
-type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	const struct type_pf_elem *d = value;
-	struct hbucket *n;
-	struct type_pf_elem *data;
-	int ret = 0, i, j = AHASH_MAX(h) + 1;
-	bool flag_exist = flags & IPSET_FLAG_EXIST;
-	u32 key, multi = 0;
-	u32 cadt_flags = flags >> 16;
-
-	if (h->elements >= h->maxelem)
-		/* FIXME: when set is full, we slow down here */
-		type_pf_expire(h, NETS_LENGTH(set->family));
-	if (h->elements >= h->maxelem) {
-		if (net_ratelimit())
-			pr_warning("Set %s is full, maxelem %u reached\n",
-				   set->name, h->maxelem);
-		return -IPSET_ERR_HASH_FULL;
-	}
-
-	rcu_read_lock_bh();
-	t = rcu_dereference_bh(h->table);
-	key = HKEY(d, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++) {
-		data = ahash_tdata(n, i);
-		if (type_pf_data_equal(data, d, &multi)) {
-			if (type_pf_data_expired(data) || flag_exist)
-				/* Just timeout value may be updated */
-				j = i;
-			else {
-				ret = -IPSET_ERR_EXIST;
-				goto out;
-			}
-		} else if (j == AHASH_MAX(h) + 1 &&
-			   type_pf_data_expired(data))
-			j = i;
-	}
-	if (j != AHASH_MAX(h) + 1) {
-		data = ahash_tdata(n, j);
-#ifdef IP_SET_HASH_WITH_NETS
-		del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family));
-		add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
-#endif
-		type_pf_data_copy(data, d);
-		type_pf_data_timeout_set(data, timeout);
-#ifdef IP_SET_HASH_WITH_NETS
-		type_pf_data_flags(data, cadt_flags);
-#endif
-		goto out;
-	}
-	TUNE_AHASH_MAX(h, multi);
-	ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), cadt_flags, timeout);
-	if (ret != 0) {
-		if (ret == -EAGAIN)
-			type_pf_data_next(h, d);
-		goto out;
-	}
-
-#ifdef IP_SET_HASH_WITH_NETS
-	add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
-#endif
-	h->elements++;
-out:
-	rcu_read_unlock_bh();
-	return ret;
-}
-
-static int
-type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	const struct type_pf_elem *d = value;
-	struct hbucket *n;
-	int i;
-	struct type_pf_elem *data;
-	u32 key, multi = 0;
-
-	key = HKEY(value, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++) {
-		data = ahash_tdata(n, i);
-		if (!type_pf_data_equal(data, d, &multi))
-			continue;
-		if (type_pf_data_expired(data))
-			return -IPSET_ERR_EXIST;
-		if (i != n->pos - 1)
-			/* Not last one */
-			type_pf_data_copy(data, ahash_tdata(n, n->pos - 1));
-
-		n->pos--;
-		h->elements--;
-#ifdef IP_SET_HASH_WITH_NETS
-		del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
-#endif
-		if (n->pos + AHASH_INIT_SIZE < n->size) {
-			void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
-					    * sizeof(struct type_pf_telem),
-					    GFP_ATOMIC);
-			if (!tmp)
-				return 0;
-			n->size -= AHASH_INIT_SIZE;
-			memcpy(tmp, n->value,
-			       n->size * sizeof(struct type_pf_telem));
-			kfree(n->value);
-			n->value = tmp;
-		}
-		return 0;
-	}
-
-	return -IPSET_ERR_EXIST;
-}
-
-#ifdef IP_SET_HASH_WITH_NETS
-static int
-type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	struct type_pf_elem *data;
-	struct hbucket *n;
-	int i, j = 0;
-	u32 key, multi = 0;
-	u8 nets_length = NETS_LENGTH(set->family);
-
-	for (; j < nets_length && h->nets[j].nets && !multi; j++) {
-		type_pf_data_netmask(d, h->nets[j].cidr);
-		key = HKEY(d, h->initval, t->htable_bits);
-		n = hbucket(t, key);
-		for (i = 0; i < n->pos; i++) {
-			data = ahash_tdata(n, i);
-#ifdef IP_SET_HASH_WITH_MULTI
-			if (type_pf_data_equal(data, d, &multi)) {
-				if (!type_pf_data_expired(data))
-					return type_pf_data_match(data);
-				multi = 0;
-			}
-#else
-			if (type_pf_data_equal(data, d, &multi) &&
-			    !type_pf_data_expired(data))
-				return type_pf_data_match(data);
-#endif
-		}
-	}
-	return 0;
-}
-#endif
-
-static int
-type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
-{
-	struct ip_set_hash *h = set->data;
-	struct htable *t = h->table;
-	struct type_pf_elem *data, *d = value;
-	struct hbucket *n;
-	int i;
-	u32 key, multi = 0;
-
-#ifdef IP_SET_HASH_WITH_NETS
-	if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
-		return type_pf_ttest_cidrs(set, d, timeout);
-#endif
-	key = HKEY(d, h->initval, t->htable_bits);
-	n = hbucket(t, key);
-	for (i = 0; i < n->pos; i++) {
-		data = ahash_tdata(n, i);
-		if (type_pf_data_equal(data, d, &multi) &&
-		    !type_pf_data_expired(data))
-			return type_pf_data_match(data);
-	}
-	return 0;
-}
-
-static int
-type_pf_tlist(const struct ip_set *set,
-	      struct sk_buff *skb, struct netlink_callback *cb)
-{
-	const struct ip_set_hash *h = set->data;
-	const struct htable *t = h->table;
-	struct nlattr *atd, *nested;
-	const struct hbucket *n;
-	const struct type_pf_elem *data;
-	u32 first = cb->args[2];
-	/* We assume that one hash bucket fills into one page */
-	void *incomplete;
-	int i;
-
-	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
-	if (!atd)
-		return -EMSGSIZE;
-	for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
-		incomplete = skb_tail_pointer(skb);
-		n = hbucket(t, cb->args[2]);
-		for (i = 0; i < n->pos; i++) {
-			data = ahash_tdata(n, i);
-			pr_debug("list %p %u\n", n, i);
-			if (type_pf_data_expired(data))
-				continue;
-			pr_debug("do list %p %u\n", n, i);
-			nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
-			if (!nested) {
-				if (cb->args[2] == first) {
-					nla_nest_cancel(skb, atd);
-					return -EMSGSIZE;
-				} else
-					goto nla_put_failure;
-			}
-			if (type_pf_data_tlist(skb, data))
-				goto nla_put_failure;
-			ipset_nest_end(skb, nested);
-		}
-	}
-	ipset_nest_end(skb, atd);
-	/* Set listing finished */
-	cb->args[2] = 0;
-
-	return 0;
-
-nla_put_failure:
-	nlmsg_trim(skb, incomplete);
-	ipset_nest_end(skb, atd);
-	if (unlikely(first == cb->args[2])) {
-		pr_warning("Can't list set %s: one bucket does not fit into "
-			   "a message. Please report it!\n", set->name);
-		cb->args[2] = 0;
-		return -EMSGSIZE;
-	}
-	return 0;
-}
-
-static const struct ip_set_type_variant type_pf_tvariant = {
-	.kadt	= type_pf_kadt,
-	.uadt	= type_pf_uadt,
-	.adt	= {
-		[IPSET_ADD] = type_pf_tadd,
-		[IPSET_DEL] = type_pf_tdel,
-		[IPSET_TEST] = type_pf_ttest,
-	},
-	.destroy = type_pf_destroy,
-	.flush	= type_pf_flush,
-	.head	= type_pf_head,
-	.list	= type_pf_tlist,
-	.resize	= type_pf_tresize,
-	.same_set = type_pf_same_set,
-};
-
-static void
-type_pf_gc(unsigned long ul_set)
-{
-	struct ip_set *set = (struct ip_set *) ul_set;
-	struct ip_set_hash *h = set->data;
-
-	pr_debug("called\n");
-	write_lock_bh(&set->lock);
-	type_pf_expire(h, NETS_LENGTH(set->family));
-	write_unlock_bh(&set->lock);
-
-	h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
-	add_timer(&h->gc);
-}
-
-static void
-type_pf_gc_init(struct ip_set *set)
-{
-	struct ip_set_hash *h = set->data;
-
-	init_timer(&h->gc);
-	h->gc.data = (unsigned long) set;
-	h->gc.function = type_pf_gc;
-	h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
-	add_timer(&h->gc);
-	pr_debug("gc initialized, run in every %u\n",
-		 IPSET_GC_PERIOD(h->timeout));
-}
-
-#undef HKEY_DATALEN
-#undef HKEY
-#undef type_pf_data_equal
-#undef type_pf_data_isnull
-#undef type_pf_data_copy
-#undef type_pf_data_zero_out
-#undef type_pf_data_netmask
-#undef type_pf_data_list
-#undef type_pf_data_tlist
-#undef type_pf_data_next
-#undef type_pf_data_flags
-#undef type_pf_data_reset_flags
-#undef type_pf_data_match
-
-#undef type_pf_elem
-#undef type_pf_telem
-#undef type_pf_data_timeout
-#undef type_pf_data_expired
-#undef type_pf_data_timeout_set
-
-#undef type_pf_elem_add
-#undef type_pf_add
-#undef type_pf_del
-#undef type_pf_test_cidrs
-#undef type_pf_test
-
-#undef type_pf_elem_tadd
-#undef type_pf_del_telem
-#undef type_pf_expire
-#undef type_pf_tadd
-#undef type_pf_tdel
-#undef type_pf_ttest_cidrs
-#undef type_pf_ttest
-
-#undef type_pf_resize
-#undef type_pf_tresize
-#undef type_pf_flush
-#undef type_pf_destroy
-#undef type_pf_head
-#undef type_pf_list
-#undef type_pf_tlist
-#undef type_pf_same_set
-#undef type_pf_kadt
-#undef type_pf_uadt
-#undef type_pf_gc
-#undef type_pf_gc_init
-#undef type_pf_variant
-#undef type_pf_tvariant
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
new file mode 100644
index 0000000..2ba7d4e
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -0,0 +1,1039 @@
+/* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _IP_SET_HASH_GEN_H
+#define _IP_SET_HASH_GEN_H
+
+#include <linux/rcupdate.h>
+#include <linux/jhash.h>
+#include <linux/netfilter/ipset/ip_set_timeout.h>
+#ifndef rcu_dereference_bh
+#define rcu_dereference_bh(p)	rcu_dereference(p)
+#endif
+
+#define CONCAT(a, b)		a##b
+#define TOKEN(a, b)		CONCAT(a, b)
+
+/* Hashing which uses arrays to resolve clashing. The hash table is resized
+ * (doubled) when searching becomes too long.
+ * Internally jhash is used with the assumption that the size of the
+ * stored data is a multiple of sizeof(u32). If storage supports timeout,
+ * the timeout field must be the last one in the data structure - that field
+ * is ignored when computing the hash key.
+ *
+ * Readers and resizing
+ *
+ * Resizing can be triggered by userspace command only, and those
+ * are serialized by the nfnl mutex. During resizing the set is
+ * read-locked, so the only possible concurrent operations are
+ * the kernel side readers. Those must be protected by proper RCU locking.
+ */
+
+/* Number of elements to store in an initial array block */
+#define AHASH_INIT_SIZE			4
+/* Max number of elements to store in an array block */
+#define AHASH_MAX_SIZE			(3*AHASH_INIT_SIZE)
+
+/* Max number of elements can be tuned */
+#ifdef IP_SET_HASH_WITH_MULTI
+#define AHASH_MAX(h)			((h)->ahash_max)
+
+static inline u8
+tune_ahash_max(u8 curr, u32 multi)
+{
+	u32 n;
+
+	if (multi < curr)
+		return curr;
+
+	n = curr + AHASH_INIT_SIZE;
+	/* Currently, at listing one hash bucket must fit into a message.
+	 * Therefore we have a hard limit here.
+	 */
+	return n > curr && n <= 64 ? n : curr;
+}
+#define TUNE_AHASH_MAX(h, multi)	\
+	((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi))
+#else
+#define AHASH_MAX(h)			AHASH_MAX_SIZE
+#define TUNE_AHASH_MAX(h, multi)
+#endif
+
+/* A hash bucket */
+struct hbucket {
+	void *value;		/* the array of the values */
+	u8 size;		/* size of the array */
+	u8 pos;			/* position of the first free entry */
+};
+
+/* The hash table: the table size stored here in order to make resizing easy */
+struct htable {
+	u8 htable_bits;		/* size of hash table == 2^htable_bits */
+	struct hbucket bucket[0]; /* hashtable buckets */
+};
+
+#define hbucket(h, i)		(&((h)->bucket[i]))
+
+/* Book-keeping of the prefixes added to the set */
+struct net_prefixes {
+	u8 cidr;		/* the different cidr values in the set */
+	u32 nets;		/* number of elements per cidr */
+};
+
+/* Compute the hash table size */
+static size_t
+htable_size(u8 hbits)
+{
+	size_t hsize;
+
+	/* We must fit both into u32 in jhash and size_t */
+	if (hbits > 31)
+		return 0;
+	hsize = jhash_size(hbits);
+	if ((((size_t)-1) - sizeof(struct htable))/sizeof(struct hbucket)
+	    < hsize)
+		return 0;
+
+	return hsize * sizeof(struct hbucket) + sizeof(struct htable);
+}
+
+/* Compute htable_bits from the user input parameter hashsize */
+static u8
+htable_bits(u32 hashsize)
+{
+	/* Assume that hashsize == 2^htable_bits */
+	u8 bits = fls(hashsize - 1);
+	if (jhash_size(bits) != hashsize)
+		/* Round up to the first 2^n value */
+		bits = fls(hashsize);
+
+	return bits;
+}
+
+/* Destroy the hashtable part of the set */
+static void
+ahash_destroy(struct htable *t)
+{
+	struct hbucket *n;
+	u32 i;
+
+	for (i = 0; i < jhash_size(t->htable_bits); i++) {
+		n = hbucket(t, i);
+		if (n->size)
+			/* FIXME: use slab cache */
+			kfree(n->value);
+	}
+
+	ip_set_free(t);
+}
+
+static int
+hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
+{
+	if (n->pos >= n->size) {
+		void *tmp;
+
+		if (n->size >= ahash_max)
+			/* Trigger rehashing */
+			return -EAGAIN;
+
+		tmp = kzalloc((n->size + AHASH_INIT_SIZE) * dsize,
+			      GFP_ATOMIC);
+		if (!tmp)
+			return -ENOMEM;
+		if (n->size) {
+			memcpy(tmp, n->value, n->size * dsize);
+			kfree(n->value);
+		}
+		n->value = tmp;
+		n->size += AHASH_INIT_SIZE;
+	}
+	return 0;
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+#ifdef IP_SET_HASH_WITH_NETS_PACKED
+/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
+#define CIDR(cidr)		(cidr + 1)
+#else
+#define CIDR(cidr)		(cidr)
+#endif
+
+#define SET_HOST_MASK(family)	(family == AF_INET ? 32 : 128)
+
+#ifdef IP_SET_HASH_WITH_MULTI
+#define NETS_LENGTH(family)	(SET_HOST_MASK(family) + 1)
+#else
+#define NETS_LENGTH(family)	SET_HOST_MASK(family)
+#endif
+
+#else
+#define NETS_LENGTH(family)	0
+#endif /* IP_SET_HASH_WITH_NETS */
+
+#define ext_timeout(e, h)	\
+(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT])
+
+#endif /* _IP_SET_HASH_GEN_H */
+
+/* Family dependent templates */
+
+#undef ahash_data
+#undef mtype_data_equal
+#undef mtype_do_data_match
+#undef mtype_data_set_flags
+#undef mtype_data_reset_flags
+#undef mtype_data_netmask
+#undef mtype_data_list
+#undef mtype_data_next
+#undef mtype_elem
+
+#undef mtype_add_cidr
+#undef mtype_del_cidr
+#undef mtype_ahash_memsize
+#undef mtype_flush
+#undef mtype_destroy
+#undef mtype_gc_init
+#undef mtype_same_set
+#undef mtype_kadt
+#undef mtype_uadt
+#undef mtype
+
+#undef mtype_add
+#undef mtype_del
+#undef mtype_test_cidrs
+#undef mtype_test
+#undef mtype_expire
+#undef mtype_resize
+#undef mtype_head
+#undef mtype_list
+#undef mtype_gc
+#undef mtype_gc_init
+#undef mtype_variant
+#undef mtype_data_match
+
+#undef HKEY
+
+#define mtype_data_equal	TOKEN(MTYPE, _data_equal)
+#ifdef IP_SET_HASH_WITH_NETS
+#define mtype_do_data_match	TOKEN(MTYPE, _do_data_match)
+#else
+#define mtype_do_data_match(d)	1
+#endif
+#define mtype_data_set_flags	TOKEN(MTYPE, _data_set_flags)
+#define mtype_data_reset_flags	TOKEN(MTYPE, _data_reset_flags)
+#define mtype_data_netmask	TOKEN(MTYPE, _data_netmask)
+#define mtype_data_list		TOKEN(MTYPE, _data_list)
+#define mtype_data_next		TOKEN(MTYPE, _data_next)
+#define mtype_elem		TOKEN(MTYPE, _elem)
+#define mtype_add_cidr		TOKEN(MTYPE, _add_cidr)
+#define mtype_del_cidr		TOKEN(MTYPE, _del_cidr)
+#define mtype_ahash_memsize	TOKEN(MTYPE, _ahash_memsize)
+#define mtype_flush		TOKEN(MTYPE, _flush)
+#define mtype_destroy		TOKEN(MTYPE, _destroy)
+#define mtype_gc_init		TOKEN(MTYPE, _gc_init)
+#define mtype_same_set		TOKEN(MTYPE, _same_set)
+#define mtype_kadt		TOKEN(MTYPE, _kadt)
+#define mtype_uadt		TOKEN(MTYPE, _uadt)
+#define mtype			MTYPE
+
+#define mtype_elem		TOKEN(MTYPE, _elem)
+#define mtype_add		TOKEN(MTYPE, _add)
+#define mtype_del		TOKEN(MTYPE, _del)
+#define mtype_test_cidrs	TOKEN(MTYPE, _test_cidrs)
+#define mtype_test		TOKEN(MTYPE, _test)
+#define mtype_expire		TOKEN(MTYPE, _expire)
+#define mtype_resize		TOKEN(MTYPE, _resize)
+#define mtype_head		TOKEN(MTYPE, _head)
+#define mtype_list		TOKEN(MTYPE, _list)
+#define mtype_gc		TOKEN(MTYPE, _gc)
+#define mtype_variant		TOKEN(MTYPE, _variant)
+#define mtype_data_match	TOKEN(MTYPE, _data_match)
+
+#ifndef HKEY_DATALEN
+#define HKEY_DATALEN		sizeof(struct mtype_elem)
+#endif
+
+#define HKEY(data, initval, htable_bits)			\
+(jhash2((u32 *)(data), HKEY_DATALEN/sizeof(u32), initval)	\
+	& jhash_mask(htable_bits))
+
+#ifndef htype
+#define htype			HTYPE
+
+/* The generic hash structure */
+struct htype {
+	struct htable *table;	/* the hash table */
+	u32 maxelem;		/* max elements in the hash */
+	u32 elements;		/* current element (vs timeout) */
+	u32 initval;		/* random jhash init value */
+	u32 timeout;		/* timeout value, if enabled */
+	size_t dsize;		/* data struct size */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
+	struct timer_list gc;	/* garbage collection when timeout enabled */
+	struct mtype_elem next; /* temporary storage for uadd */
+#ifdef IP_SET_HASH_WITH_MULTI
+	u8 ahash_max;		/* max elements in an array block */
+#endif
+#ifdef IP_SET_HASH_WITH_NETMASK
+	u8 netmask;		/* netmask value for subnets to store */
+#endif
+#ifdef IP_SET_HASH_WITH_RBTREE
+	struct rb_root rbtree;
+#endif
+#ifdef IP_SET_HASH_WITH_NETS
+	struct net_prefixes nets[0]; /* book-keeping of prefixes */
+#endif
+};
+#endif
+
+#ifdef IP_SET_HASH_WITH_NETS
+/* Network cidr size book keeping when the hash stores different
+ * sized networks */
+static void
+mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length)
+{
+	int i, j;
+
+	/* Add in increasing prefix order, so larger cidr first */
+	for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) {
+		if (j != -1)
+			continue;
+		else if (h->nets[i].cidr < cidr)
+			j = i;
+		else if (h->nets[i].cidr == cidr) {
+			h->nets[i].nets++;
+			return;
+		}
+	}
+	if (j != -1) {
+		for (; i > j; i--) {
+			h->nets[i].cidr = h->nets[i - 1].cidr;
+			h->nets[i].nets = h->nets[i - 1].nets;
+		}
+	}
+	h->nets[i].cidr = cidr;
+	h->nets[i].nets = 1;
+}
+
+static void
+mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length)
+{
+	u8 i, j;
+
+	for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++)
+		;
+	h->nets[i].nets--;
+
+	if (h->nets[i].nets != 0)
+		return;
+
+	for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) {
+		h->nets[j].cidr = h->nets[j + 1].cidr;
+		h->nets[j].nets = h->nets[j + 1].nets;
+	}
+}
+#endif
+
+/* Calculate the actual memory size of the set data */
+static size_t
+mtype_ahash_memsize(const struct htype *h, u8 nets_length)
+{
+	u32 i;
+	struct htable *t = h->table;
+	size_t memsize = sizeof(*h)
+			 + sizeof(*t)
+#ifdef IP_SET_HASH_WITH_NETS
+			 + sizeof(struct net_prefixes) * nets_length
+#endif
+			 + jhash_size(t->htable_bits) * sizeof(struct hbucket);
+
+	for (i = 0; i < jhash_size(t->htable_bits); i++)
+		memsize += t->bucket[i].size * h->dsize;
+
+	return memsize;
+}
+
+/* Flush a hash type of set: destroy all elements */
+static void
+mtype_flush(struct ip_set *set)
+{
+	struct htype *h = set->data;
+	struct htable *t = h->table;
+	struct hbucket *n;
+	u32 i;
+
+	for (i = 0; i < jhash_size(t->htable_bits); i++) {
+		n = hbucket(t, i);
+		if (n->size) {
+			n->size = n->pos = 0;
+			/* FIXME: use slab cache */
+			kfree(n->value);
+		}
+	}
+#ifdef IP_SET_HASH_WITH_NETS
+	memset(h->nets, 0, sizeof(struct net_prefixes)
+			   * NETS_LENGTH(set->family));
+#endif
+	h->elements = 0;
+}
+
+/* Destroy a hash type of set */
+static void
+mtype_destroy(struct ip_set *set)
+{
+	struct htype *h = set->data;
+
+	if (set->extensions & IPSET_EXT_TIMEOUT)
+		del_timer_sync(&h->gc);
+
+	ahash_destroy(h->table);
+#ifdef IP_SET_HASH_WITH_RBTREE
+	rbtree_destroy(&h->rbtree);
+#endif
+	kfree(h);
+
+	set->data = NULL;
+}
+
+static void
+mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
+{
+	struct htype *h = set->data;
+
+	init_timer(&h->gc);
+	h->gc.data = (unsigned long) set;
+	h->gc.function = gc;
+	h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+	add_timer(&h->gc);
+	pr_debug("gc initialized, run in every %u\n",
+		 IPSET_GC_PERIOD(h->timeout));
+}
+
+static bool
+mtype_same_set(const struct ip_set *a, const struct ip_set *b)
+{
+	const struct htype *x = a->data;
+	const struct htype *y = b->data;
+
+	/* Resizing changes htable_bits, so we ignore it */
+	return x->maxelem == y->maxelem &&
+	       x->timeout == y->timeout &&
+#ifdef IP_SET_HASH_WITH_NETMASK
+	       x->netmask == y->netmask &&
+#endif
+	       a->extensions == b->extensions;
+}
+
+/* Get the ith element from the array block n */
+#define ahash_data(n, i, dsize)	\
+	((struct mtype_elem *)((n)->value + ((i) * (dsize))))
+
+/* Delete expired elements from the hashtable */
+static void
+mtype_expire(struct htype *h, u8 nets_length, size_t dsize)
+{
+	struct htable *t = h->table;
+	struct hbucket *n;
+	struct mtype_elem *data;
+	u32 i;
+	int j;
+
+	for (i = 0; i < jhash_size(t->htable_bits); i++) {
+		n = hbucket(t, i);
+		for (j = 0; j < n->pos; j++) {
+			data = ahash_data(n, j, dsize);
+			if (ip_set_timeout_expired(ext_timeout(data, h))) {
+				pr_debug("expired %u/%u\n", i, j);
+#ifdef IP_SET_HASH_WITH_NETS
+				mtype_del_cidr(h, CIDR(data->cidr),
+					       nets_length);
+#endif
+				if (j != n->pos - 1)
+					/* Not last one */
+					memcpy(data,
+					       ahash_data(n, n->pos - 1, dsize),
+					       dsize);
+				n->pos--;
+				h->elements--;
+			}
+		}
+		if (n->pos + AHASH_INIT_SIZE < n->size) {
+			void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
+					    * dsize,
+					    GFP_ATOMIC);
+			if (!tmp)
+				/* Still try to delete expired elements */
+				continue;
+			n->size -= AHASH_INIT_SIZE;
+			memcpy(tmp, n->value, n->size * dsize);
+			kfree(n->value);
+			n->value = tmp;
+		}
+	}
+}
+
+static void
+mtype_gc(unsigned long ul_set)
+{
+	struct ip_set *set = (struct ip_set *) ul_set;
+	struct htype *h = set->data;
+
+	pr_debug("called\n");
+	write_lock_bh(&set->lock);
+	mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+	write_unlock_bh(&set->lock);
+
+	h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ;
+	add_timer(&h->gc);
+}
+
+/* Resize a hash: create a new hash table with doubling the hashsize
+ * and inserting the elements to it. Repeat until we succeed or
+ * fail due to memory pressures. */
+static int
+mtype_resize(struct ip_set *set, bool retried)
+{
+	struct htype *h = set->data;
+	struct htable *t, *orig = h->table;
+	u8 htable_bits = orig->htable_bits;
+#ifdef IP_SET_HASH_WITH_NETS
+	u8 flags;
+#endif
+	struct mtype_elem *data;
+	struct mtype_elem *d;
+	struct hbucket *n, *m;
+	u32 i, j;
+	int ret;
+
+	/* Try to cleanup once */
+	if (SET_WITH_TIMEOUT(set) && !retried) {
+		i = h->elements;
+		write_lock_bh(&set->lock);
+		mtype_expire(set->data, NETS_LENGTH(set->family),
+			     h->dsize);
+		write_unlock_bh(&set->lock);
+		if (h->elements < i)
+			return 0;
+	}
+
+retry:
+	ret = 0;
+	htable_bits++;
+	pr_debug("attempt to resize set %s from %u to %u, t %p\n",
+		 set->name, orig->htable_bits, htable_bits, orig);
+	if (!htable_bits) {
+		/* In case we have plenty of memory :-) */
+		pr_warning("Cannot increase the hashsize of set %s further\n",
+			   set->name);
+		return -IPSET_ERR_HASH_FULL;
+	}
+	t = ip_set_alloc(sizeof(*t)
+			 + jhash_size(htable_bits) * sizeof(struct hbucket));
+	if (!t)
+		return -ENOMEM;
+	t->htable_bits = htable_bits;
+
+	read_lock_bh(&set->lock);
+	for (i = 0; i < jhash_size(orig->htable_bits); i++) {
+		n = hbucket(orig, i);
+		for (j = 0; j < n->pos; j++) {
+			data = ahash_data(n, j, h->dsize);
+#ifdef IP_SET_HASH_WITH_NETS
+			flags = 0;
+			mtype_data_reset_flags(data, &flags);
+#endif
+			m = hbucket(t, HKEY(data, h->initval, htable_bits));
+			ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize);
+			if (ret < 0) {
+#ifdef IP_SET_HASH_WITH_NETS
+				mtype_data_reset_flags(data, &flags);
+#endif
+				read_unlock_bh(&set->lock);
+				ahash_destroy(t);
+				if (ret == -EAGAIN)
+					goto retry;
+				return ret;
+			}
+			d = ahash_data(m, m->pos++, h->dsize);
+			memcpy(d, data, h->dsize);
+#ifdef IP_SET_HASH_WITH_NETS
+			mtype_data_reset_flags(d, &flags);
+#endif
+		}
+	}
+
+	rcu_assign_pointer(h->table, t);
+	read_unlock_bh(&set->lock);
+
+	/* Give time to other readers of the set */
+	synchronize_rcu_bh();
+
+	pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name,
+		 orig->htable_bits, orig, t->htable_bits, t);
+	ahash_destroy(orig);
+
+	return 0;
+}
+
+/* Add an element to a hash and update the internal counters when succeeded,
+ * otherwise report the proper error code. */
+static int
+mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	  struct ip_set_ext *mext, u32 flags)
+{
+	struct htype *h = set->data;
+	struct htable *t;
+	const struct mtype_elem *d = value;
+	struct mtype_elem *data;
+	struct hbucket *n;
+	int i, ret = 0;
+	int j = AHASH_MAX(h) + 1;
+	bool flag_exist = flags & IPSET_FLAG_EXIST;
+	u32 key, multi = 0;
+
+	if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
+		/* FIXME: when set is full, we slow down here */
+		mtype_expire(h, NETS_LENGTH(set->family), h->dsize);
+
+	if (h->elements >= h->maxelem) {
+		if (net_ratelimit())
+			pr_warning("Set %s is full, maxelem %u reached\n",
+				   set->name, h->maxelem);
+		return -IPSET_ERR_HASH_FULL;
+	}
+
+	rcu_read_lock_bh();
+	t = rcu_dereference_bh(h->table);
+	key = HKEY(value, h->initval, t->htable_bits);
+	n = hbucket(t, key);
+	for (i = 0; i < n->pos; i++) {
+		data = ahash_data(n, i, h->dsize);
+		if (mtype_data_equal(data, d, &multi)) {
+			if (flag_exist ||
+			    (SET_WITH_TIMEOUT(set) &&
+			     ip_set_timeout_expired(ext_timeout(data, h)))) {
+				/* Just the extensions could be overwritten */
+				j = i;
+				goto reuse_slot;
+			} else {
+				ret = -IPSET_ERR_EXIST;
+				goto out;
+			}
+		}
+		/* Reuse first timed out entry */
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(data, h)) &&
+		    j != AHASH_MAX(h) + 1)
+			j = i;
+	}
+reuse_slot:
+	if (j != AHASH_MAX(h) + 1) {
+		/* Fill out reused slot */
+		data = ahash_data(n, j, h->dsize);
+#ifdef IP_SET_HASH_WITH_NETS
+		mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family));
+		mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+#endif
+	} else {
+		/* Use/create a new slot */
+		TUNE_AHASH_MAX(h, multi);
+		ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize);
+		if (ret != 0) {
+			if (ret == -EAGAIN)
+				mtype_data_next(&h->next, d);
+			goto out;
+		}
+		data = ahash_data(n, n->pos++, h->dsize);
+#ifdef IP_SET_HASH_WITH_NETS
+		mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+#endif
+		h->elements++;
+	}
+	memcpy(data, d, sizeof(struct mtype_elem));
+#ifdef IP_SET_HASH_WITH_NETS
+	mtype_data_set_flags(data, flags);
+#endif
+	if (SET_WITH_TIMEOUT(set))
+		ip_set_timeout_set(ext_timeout(data, h), ext->timeout);
+
+out:
+	rcu_read_unlock_bh();
+	return ret;
+}
+
+/* Delete an element from the hash: swap it with the last element
+ * and free up space if possible.
+ */
+static int
+mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	  struct ip_set_ext *mext, u32 flags)
+{
+	struct htype *h = set->data;
+	struct htable *t = h->table;
+	const struct mtype_elem *d = value;
+	struct mtype_elem *data;
+	struct hbucket *n;
+	int i;
+	u32 key, multi = 0;
+
+	key = HKEY(value, h->initval, t->htable_bits);
+	n = hbucket(t, key);
+	for (i = 0; i < n->pos; i++) {
+		data = ahash_data(n, i, h->dsize);
+		if (!mtype_data_equal(data, d, &multi))
+			continue;
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(data, h)))
+			return -IPSET_ERR_EXIST;
+		if (i != n->pos - 1)
+			/* Not last one */
+			memcpy(data, ahash_data(n, n->pos - 1, h->dsize),
+			       h->dsize);
+
+		n->pos--;
+		h->elements--;
+#ifdef IP_SET_HASH_WITH_NETS
+		mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family));
+#endif
+		if (n->pos + AHASH_INIT_SIZE < n->size) {
+			void *tmp = kzalloc((n->size - AHASH_INIT_SIZE)
+					    * h->dsize,
+					    GFP_ATOMIC);
+			if (!tmp)
+				return 0;
+			n->size -= AHASH_INIT_SIZE;
+			memcpy(tmp, n->value, n->size * h->dsize);
+			kfree(n->value);
+			n->value = tmp;
+		}
+		return 0;
+	}
+
+	return -IPSET_ERR_EXIST;
+}
+
+static inline int
+mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext,
+		 struct ip_set_ext *mext, struct ip_set *set, u32 flags)
+{
+	return mtype_do_data_match(data);
+}
+
+#ifdef IP_SET_HASH_WITH_NETS
+/* Special test function which takes into account the different network
+ * sizes added to the set */
+static int
+mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
+		 const struct ip_set_ext *ext,
+		 struct ip_set_ext *mext, u32 flags)
+{
+	struct htype *h = set->data;
+	struct htable *t = h->table;
+	struct hbucket *n;
+	struct mtype_elem *data;
+	int i, j = 0;
+	u32 key, multi = 0;
+	u8 nets_length = NETS_LENGTH(set->family);
+
+	pr_debug("test by nets\n");
+	for (; j < nets_length && h->nets[j].nets && !multi; j++) {
+		mtype_data_netmask(d, h->nets[j].cidr);
+		key = HKEY(d, h->initval, t->htable_bits);
+		n = hbucket(t, key);
+		for (i = 0; i < n->pos; i++) {
+			data = ahash_data(n, i, h->dsize);
+			if (!mtype_data_equal(data, d, &multi))
+				continue;
+			if (SET_WITH_TIMEOUT(set)) {
+				if (!ip_set_timeout_expired(
+							ext_timeout(data, h)))
+					return mtype_data_match(data, ext,
+								mext, set,
+								flags);
+#ifdef IP_SET_HASH_WITH_MULTI
+				multi = 0;
+#endif
+			} else
+				return mtype_data_match(data, ext,
+							mext, set, flags);
+		}
+	}
+	return 0;
+}
+#endif
+
+/* Test whether the element is added to the set */
+static int
+mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	   struct ip_set_ext *mext, u32 flags)
+{
+	struct htype *h = set->data;
+	struct htable *t = h->table;
+	struct mtype_elem *d = value;
+	struct hbucket *n;
+	struct mtype_elem *data;
+	int i;
+	u32 key, multi = 0;
+
+#ifdef IP_SET_HASH_WITH_NETS
+	/* If we test an IP address and not a network address,
+	 * try all possible network sizes */
+	if (CIDR(d->cidr) == SET_HOST_MASK(set->family))
+		return mtype_test_cidrs(set, d, ext, mext, flags);
+#endif
+
+	key = HKEY(d, h->initval, t->htable_bits);
+	n = hbucket(t, key);
+	for (i = 0; i < n->pos; i++) {
+		data = ahash_data(n, i, h->dsize);
+		if (mtype_data_equal(data, d, &multi) &&
+		    !(SET_WITH_TIMEOUT(set) &&
+		      ip_set_timeout_expired(ext_timeout(data, h))))
+			return mtype_data_match(data, ext, mext, set, flags);
+	}
+	return 0;
+}
+
+/* Reply a HEADER request: fill out the header part of the set */
+static int
+mtype_head(struct ip_set *set, struct sk_buff *skb)
+{
+	const struct htype *h = set->data;
+	struct nlattr *nested;
+	size_t memsize;
+
+	read_lock_bh(&set->lock);
+	memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family));
+	read_unlock_bh(&set->lock);
+
+	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+	if (!nested)
+		goto nla_put_failure;
+	if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
+			  htonl(jhash_size(h->table->htable_bits))) ||
+	    nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
+		goto nla_put_failure;
+#ifdef IP_SET_HASH_WITH_NETMASK
+	if (h->netmask != HOST_MASK &&
+	    nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
+		goto nla_put_failure;
+#endif
+	if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
+	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
+	    ((set->extensions & IPSET_EXT_TIMEOUT) &&
+	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))))
+		goto nla_put_failure;
+	ipset_nest_end(skb, nested);
+
+	return 0;
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+/* Reply a LIST/SAVE request: dump the elements of the specified set */
+static int
+mtype_list(const struct ip_set *set,
+	   struct sk_buff *skb, struct netlink_callback *cb)
+{
+	const struct htype *h = set->data;
+	const struct htable *t = h->table;
+	struct nlattr *atd, *nested;
+	const struct hbucket *n;
+	const struct mtype_elem *e;
+	u32 first = cb->args[2];
+	/* We assume that one hash bucket fills into one page */
+	void *incomplete;
+	int i;
+
+	atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
+	if (!atd)
+		return -EMSGSIZE;
+	pr_debug("list hash set %s\n", set->name);
+	for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) {
+		incomplete = skb_tail_pointer(skb);
+		n = hbucket(t, cb->args[2]);
+		pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n);
+		for (i = 0; i < n->pos; i++) {
+			e = ahash_data(n, i, h->dsize);
+			if (SET_WITH_TIMEOUT(set) &&
+			    ip_set_timeout_expired(ext_timeout(e, h)))
+				continue;
+			pr_debug("list hash %lu hbucket %p i %u, data %p\n",
+				 cb->args[2], n, i, e);
+			nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+			if (!nested) {
+				if (cb->args[2] == first) {
+					nla_nest_cancel(skb, atd);
+					return -EMSGSIZE;
+				} else
+					goto nla_put_failure;
+			}
+			if (mtype_data_list(skb, e))
+				goto nla_put_failure;
+			if (SET_WITH_TIMEOUT(set) &&
+			    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+					  htonl(ip_set_timeout_get(
+						ext_timeout(e, h)))))
+				goto nla_put_failure;
+			ipset_nest_end(skb, nested);
+		}
+	}
+	ipset_nest_end(skb, atd);
+	/* Set listing finished */
+	cb->args[2] = 0;
+
+	return 0;
+
+nla_put_failure:
+	nlmsg_trim(skb, incomplete);
+	ipset_nest_end(skb, atd);
+	if (unlikely(first == cb->args[2])) {
+		pr_warning("Can't list set %s: one bucket does not fit into "
+			   "a message. Please report it!\n", set->name);
+		cb->args[2] = 0;
+		return -EMSGSIZE;
+	}
+	return 0;
+}
+
+static int
+TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb,
+	      const struct xt_action_param *par,
+	      enum ipset_adt adt, struct ip_set_adt_opt *opt);
+
+static int
+TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[],
+	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
+
+static const struct ip_set_type_variant mtype_variant = {
+	.kadt	= mtype_kadt,
+	.uadt	= mtype_uadt,
+	.adt	= {
+		[IPSET_ADD] = mtype_add,
+		[IPSET_DEL] = mtype_del,
+		[IPSET_TEST] = mtype_test,
+	},
+	.destroy = mtype_destroy,
+	.flush	= mtype_flush,
+	.head	= mtype_head,
+	.list	= mtype_list,
+	.resize	= mtype_resize,
+	.same_set = mtype_same_set,
+};
+
+#ifdef IP_SET_EMIT_CREATE
+static int
+TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
+{
+	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+	u8 hbits;
+#ifdef IP_SET_HASH_WITH_NETMASK
+	u8 netmask;
+#endif
+	size_t hsize;
+	struct HTYPE *h;
+
+	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
+		return -IPSET_ERR_INVALID_FAMILY;
+#ifdef IP_SET_HASH_WITH_NETMASK
+	netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
+	pr_debug("Create set %s with family %s\n",
+		 set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6");
+#endif
+
+	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		return -IPSET_ERR_PROTOCOL;
+
+	if (tb[IPSET_ATTR_HASHSIZE]) {
+		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
+		if (hashsize < IPSET_MIMINAL_HASHSIZE)
+			hashsize = IPSET_MIMINAL_HASHSIZE;
+	}
+
+	if (tb[IPSET_ATTR_MAXELEM])
+		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
+
+#ifdef IP_SET_HASH_WITH_NETMASK
+	if (tb[IPSET_ATTR_NETMASK]) {
+		netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
+
+		if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
+		    (set->family == NFPROTO_IPV6 && netmask > 128) ||
+		    netmask == 0)
+			return -IPSET_ERR_INVALID_NETMASK;
+	}
+#endif
+
+	hsize = sizeof(*h);
+#ifdef IP_SET_HASH_WITH_NETS
+	hsize += sizeof(struct net_prefixes) *
+		(set->family == NFPROTO_IPV4 ? 32 : 128);
+#endif
+	h = kzalloc(hsize, GFP_KERNEL);
+	if (!h)
+		return -ENOMEM;
+
+	h->maxelem = maxelem;
+#ifdef IP_SET_HASH_WITH_NETMASK
+	h->netmask = netmask;
+#endif
+	get_random_bytes(&h->initval, sizeof(h->initval));
+	h->timeout = IPSET_NO_TIMEOUT;
+
+	hbits = htable_bits(hashsize);
+	hsize = htable_size(hbits);
+	if (hsize == 0) {
+		kfree(h);
+		return -ENOMEM;
+	}
+	h->table = ip_set_alloc(hsize);
+	if (!h->table) {
+		kfree(h);
+		return -ENOMEM;
+	}
+	h->table->htable_bits = hbits;
+
+	set->data = h;
+	if (set->family ==  NFPROTO_IPV4)
+		set->variant = &TOKEN(HTYPE, 4_variant);
+	else
+		set->variant = &TOKEN(HTYPE, 6_variant);
+
+	if (tb[IPSET_ATTR_TIMEOUT]) {
+		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+		set->extensions |= IPSET_EXT_TIMEOUT;
+		if (set->family == NFPROTO_IPV4) {
+			h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem));
+			h->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct TOKEN(HTYPE, 4t_elem),
+					 timeout);
+			TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc));
+		} else {
+			h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem));
+			h->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct TOKEN(HTYPE, 6t_elem),
+					 timeout);
+			TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc));
+		}
+	} else {
+		if (set->family == NFPROTO_IPV4)
+			h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem));
+		else
+			h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem));
+	}
+
+	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
+		 set->name, jhash_size(h->table->htable_bits),
+		 h->table->htable_bits, h->maxelem, set->data, h->table);
+
+	return 0;
+}
+#endif /* IP_SET_EMIT_CREATE */
-- 
1.7.10.4

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

* [PATCH 07/18] netfilter: ipset: Hash types using the unified code base
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (5 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 06/18] netfilter: ipset: Unified hash type generation Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 08/18] netfilter: ipset: list:set type using the extension interface Pablo Neira Ayuso
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_hash_ip.c        |  306 +++++---------------
 net/netfilter/ipset/ip_set_hash_ipport.c    |  320 +++++----------------
 net/netfilter/ipset/ip_set_hash_ipportip.c  |  322 +++++----------------
 net/netfilter/ipset/ip_set_hash_ipportnet.c |  399 +++++++-------------------
 net/netfilter/ipset/ip_set_hash_net.c       |  340 ++++++-----------------
 net/netfilter/ipset/ip_set_hash_netiface.c  |  400 +++++++--------------------
 net/netfilter/ipset/ip_set_hash_netport.c   |  388 +++++++-------------------
 7 files changed, 610 insertions(+), 1865 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index c486ad2..2fcfb21 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
@@ -33,58 +32,36 @@ IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:ip");
 
 /* Type specific function prefix */
-#define TYPE		hash_ip
-
-static bool
-hash_ip_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_ip4_same_set	hash_ip_same_set
-#define hash_ip6_same_set	hash_ip_same_set
+#define HTYPE		hash_ip
+#define IP_SET_HASH_WITH_NETMASK
 
-/* The type variant functions: IPv4 */
+/* IPv4 variants */
 
-/* Member elements without timeout */
+/* Member elements */
 struct hash_ip4_elem {
+	/* Zero valued IP addresses cannot be stored */
 	__be32 ip;
 };
 
-/* Member elements with timeout support */
-struct hash_ip4_telem {
+struct hash_ip4t_elem {
 	__be32 ip;
 	unsigned long timeout;
 };
 
-static inline bool
-hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
-		    const struct hash_ip4_elem *ip2,
-		    u32 *multi)
-{
-	return ip1->ip == ip2->ip;
-}
+/* Common functions */
 
 static inline bool
-hash_ip4_data_isnull(const struct hash_ip4_elem *elem)
-{
-	return elem->ip == 0;
-}
-
-static inline void
-hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src)
-{
-	dst->ip = src->ip;
-}
-
-/* Zero valued IP addresses cannot be stored */
-static inline void
-hash_ip4_data_zero_out(struct hash_ip4_elem *elem)
+hash_ip4_data_equal(const struct hash_ip4_elem *e1,
+		    const struct hash_ip4_elem *e2,
+		    u32 *multi)
 {
-	elem->ip = 0;
+	return e1->ip == e2->ip;
 }
 
 static inline bool
-hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data)
+hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *e)
 {
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip))
+	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, e->ip))
 		goto nla_put_failure;
 	return 0;
 
@@ -92,41 +69,26 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data)
+static inline void
+hash_ip4_data_next(struct hash_ip4_elem *next, const struct hash_ip4_elem *e)
 {
-	const struct hash_ip4_telem *tdata =
-		(const struct hash_ip4_telem *)data;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))))
-		goto nla_put_failure;
-
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = e->ip;
 }
 
-#define IP_SET_HASH_WITH_NETMASK
+#define MTYPE		hash_ip4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ip4_data_next(struct ip_set_hash *h, const struct hash_ip4_elem *d)
-{
-	h->next.ip = d->ip;
-}
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
 	      const struct xt_action_param *par,
-	      enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
+	struct hash_ip4_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 	__be32 ip;
 
 	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
@@ -134,17 +96,19 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
 	if (ip == 0)
 		return -EINVAL;
 
-	return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
+	e.ip = ip;
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	u32 ip, ip_to, hosts, timeout = h->timeout;
-	__be32 nip;
+	struct hash_ip4_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
+	u32 ip, ip_to, hosts;
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -154,23 +118,18 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	ip &= ip_set_hostmask(h->netmask);
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
-
 	if (adt == IPSET_TEST) {
-		nip = htonl(ip);
-		if (nip == 0)
+		e.ip = htonl(ip);
+		if (e.ip == 0)
 			return -IPSET_ERR_HASH_ELEM;
-		return adtfn(set, &nip, timeout, flags);
+		return adtfn(set, &e, &ext, &ext, flags);
 	}
 
 	ip_to = ip;
@@ -193,10 +152,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		ip = ntohl(h->next.ip);
 	for (; !before(ip_to, ip); ip += hosts) {
-		nip = htonl(ip);
-		if (nip == 0)
+		e.ip = htonl(ip);
+		if (e.ip == 0)
 			return -IPSET_ERR_HASH_ELEM;
-		ret = adtfn(set, &nip, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -206,29 +165,20 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_ip_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout &&
-	       x->netmask == y->netmask;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
+/* Member elements */
 struct hash_ip6_elem {
 	union nf_inet_addr ip;
 };
 
-struct hash_ip6_telem {
+struct hash_ip6t_elem {
 	union nf_inet_addr ip;
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
 		    const struct hash_ip6_elem *ip2,
@@ -237,28 +187,16 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
 	return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
 }
 
-static inline bool
-hash_ip6_data_isnull(const struct hash_ip6_elem *elem)
-{
-	return ipv6_addr_any(&elem->ip.in6);
-}
-
 static inline void
-hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src)
+hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
 {
-	dst->ip.in6 = src->ip.in6;
-}
-
-static inline void
-hash_ip6_data_zero_out(struct hash_ip6_elem *elem)
-{
-	ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0);
+	ip6_netmask(ip, prefix);
 }
 
 static bool
-hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data)
+hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
 {
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6))
+	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6))
 		goto nla_put_failure;
 	return 0;
 
@@ -266,65 +204,49 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data)
+static inline void
+hash_ip6_data_next(struct hash_ip4_elem *next, const struct hash_ip6_elem *e)
 {
-	const struct hash_ip6_telem *e =
-		(const struct hash_ip6_telem *)data;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
+#undef HKEY_DATALEN
 
+#define MTYPE		hash_ip6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
 
-static inline void
-hash_ip6_data_next(struct ip_set_hash *h, const struct hash_ip6_elem *d)
-{
-}
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
 	      const struct xt_action_param *par,
-	      enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	union nf_inet_addr ip;
+	struct hash_ip6_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip.in6);
-	ip6_netmask(&ip, h->netmask);
-	if (ipv6_addr_any(&ip.in6))
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	hash_ip6_netmask(&e.ip, h->netmask);
+	if (ipv6_addr_any(&e.ip.in6))
 		return -EINVAL;
 
-	return adtfn(set, &ip, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
-static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = {
-	[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
-	[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
-	[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
-};
-
 static int
 hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
 	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	union nf_inet_addr ip;
-	u32 timeout = h->timeout;
+	struct hash_ip6_elem e = {};
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	int ret;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -336,110 +258,20 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
-	ip6_netmask(&ip, h->netmask);
-	if (ipv6_addr_any(&ip.in6))
+	hash_ip6_netmask(&e.ip, h->netmask);
+	if (ipv6_addr_any(&e.ip.in6))
 		return -IPSET_ERR_HASH_ELEM;
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
-
-	ret = adtfn(set, &ip, timeout, flags);
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 	return ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 netmask, hbits;
-	size_t hsize;
-	struct ip_set_hash *h;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-	netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
-	pr_debug("Create set %s with family %s\n",
-		 set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6");
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	if (tb[IPSET_ATTR_NETMASK]) {
-		netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
-
-		if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
-		    (set->family == NFPROTO_IPV6 && netmask > 128) ||
-		    netmask == 0)
-			return -IPSET_ERR_INVALID_NETMASK;
-	}
-
-	h = kzalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	h->netmask = netmask;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ip4_tvariant : &hash_ip6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_ip4_gc_init(set);
-		else
-			hash_ip6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ip4_variant : &hash_ip6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_ip_type __read_mostly = {
 	.name		= "hash:ip",
 	.protocol	= IPSET_PROTOCOL,
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index d8f77ba..d89cf41 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
@@ -34,17 +33,11 @@ IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port");
 
 /* Type specific function prefix */
-#define TYPE		hash_ipport
+#define HTYPE		hash_ipport
 
-static bool
-hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_ipport4_same_set	hash_ipport_same_set
-#define hash_ipport6_same_set	hash_ipport_same_set
-
-/* The type variant functions: IPv4 */
+/* IPv4 variants */
 
-/* Member elements without timeout */
+/* Member elements */
 struct hash_ipport4_elem {
 	__be32 ip;
 	__be16 port;
@@ -52,8 +45,7 @@ struct hash_ipport4_elem {
 	u8 padding;
 };
 
-/* Member elements with timeout support */
-struct hash_ipport4_telem {
+struct hash_ipport4t_elem {
 	__be32 ip;
 	__be16 port;
 	u8 proto;
@@ -61,6 +53,8 @@ struct hash_ipport4_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
 			const struct hash_ipport4_elem *ip2,
@@ -71,27 +65,6 @@ hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipport4_data_isnull(const struct hash_ipport4_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_ipport4_data_copy(struct hash_ipport4_elem *dst,
-		       const struct hash_ipport4_elem *src)
-{
-	dst->ip = src->ip;
-	dst->port = src->port;
-	dst->proto = src->proto;
-}
-
-static inline void
-hash_ipport4_data_zero_out(struct hash_ipport4_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_ipport4_data_list(struct sk_buff *skb,
 		       const struct hash_ipport4_elem *data)
@@ -106,64 +79,47 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipport4_data_tlist(struct sk_buff *skb,
-			const struct hash_ipport4_elem *data)
-{
-	const struct hash_ipport4_telem *tdata =
-		(const struct hash_ipport4_telem *)data;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
-}
-
-#define PF		4
-#define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
 static inline void
-hash_ipport4_data_next(struct ip_set_hash *h,
+hash_ipport4_data_next(struct hash_ipport4_elem *next,
 		       const struct hash_ipport4_elem *d)
 {
-	h->next.ip = d->ip;
-	h->next.port = d->port;
+	next->ip = d->ip;
+	next->port = d->port;
 }
 
+#define MTYPE           hash_ipport4
+#define PF              4
+#define HOST_MASK       32
+#define HKEY_DATALEN	sizeof(struct hash_ipport4_elem)
+#include "ip_set_hash_gen.h"
+
 static int
 hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
 		  const struct xt_action_param *par,
-		  enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipport4_elem data = { };
+	struct hash_ipport4_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		  enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipport4_elem data = { };
+	struct hash_ipport4_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 ip, ip_to, p = 0, port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	int ret;
 
@@ -176,41 +132,36 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMP))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMP))
+		e.port = 0;
 
 	if (adt == IPSET_TEST ||
 	    !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
 	      tb[IPSET_ATTR_PORT_TO])) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	ip_to = ip = ntohl(data.ip);
+	ip_to = ip = ntohl(e.ip);
 	if (tb[IPSET_ATTR_IP_TO]) {
 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
 		if (ret)
@@ -225,7 +176,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		ip_set_mask_from_to(ip, ip_to, cidr);
 	}
 
-	port_to = port = ntohs(data.port);
+	port_to = port = ntohs(e.port);
 	if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 		if (port > port_to)
@@ -238,9 +189,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
 						       : port;
 		for (; p <= port_to; p++) {
-			data.ip = htonl(ip);
-			data.port = htons(p);
-			ret = adtfn(set, &data, timeout, flags);
+			e.ip = htonl(ip);
+			e.port = htons(p);
+			ret = adtfn(set, &e, &ext, &ext, flags);
 
 			if (ret && !ip_set_eexist(ret, flags))
 				return ret;
@@ -251,18 +202,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_ipport_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_ipport6_elem {
 	union nf_inet_addr ip;
@@ -271,7 +211,7 @@ struct hash_ipport6_elem {
 	u8 padding;
 };
 
-struct hash_ipport6_telem {
+struct hash_ipport6t_elem {
 	union nf_inet_addr ip;
 	__be16 port;
 	u8 proto;
@@ -279,6 +219,8 @@ struct hash_ipport6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
 			const struct hash_ipport6_elem *ip2,
@@ -289,25 +231,6 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipport6_data_isnull(const struct hash_ipport6_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_ipport6_data_copy(struct hash_ipport6_elem *dst,
-		       const struct hash_ipport6_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_ipport6_data_zero_out(struct hash_ipport6_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_ipport6_data_list(struct sk_buff *skb,
 		       const struct hash_ipport6_elem *data)
@@ -322,66 +245,52 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipport6_data_tlist(struct sk_buff *skb,
-			const struct hash_ipport6_elem *data)
+static inline void
+hash_ipport6_data_next(struct hash_ipport4_elem *next,
+		       const struct hash_ipport6_elem *d)
 {
-	const struct hash_ipport6_telem *e =
-		(const struct hash_ipport6_telem *)data;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->port = d->port;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
+#undef HKEY_DATALEN
 
+#define MTYPE		hash_ipport6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ipport6_data_next(struct ip_set_hash *h,
-		       const struct hash_ipport6_elem *d)
-{
-	h->next.port = d->port;
-}
+#define HKEY_DATALEN	sizeof(struct hash_ipport6_elem)
+#define	IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
 		  const struct xt_action_param *par,
-		  enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipport6_elem data = { };
+	struct hash_ipport6_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		  enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipport6_elem data = { };
+	struct hash_ipport6_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	int ret;
 
@@ -396,39 +305,34 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMPV6))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+		e.port = 0;
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	port = ntohs(data.port);
+	port = ntohs(e.port);
 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 	if (port > port_to)
 		swap(port, port_to);
@@ -436,8 +340,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		port = ntohs(h->next.port);
 	for (; port <= port_to; port++) {
-		data.port = htons(port);
-		ret = adtfn(set, &data, timeout, flags);
+		e.port = htons(port);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -447,78 +351,6 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	struct ip_set_hash *h;
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipport4_tvariant : &hash_ipport6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_ipport4_gc_init(set);
-		else
-			hash_ipport6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipport4_variant : &hash_ipport6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_ipport_type __read_mostly = {
 	.name		= "hash:ip,port",
 	.protocol	= IPSET_PROTOCOL,
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index 1da1e95..4b58e5c 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
@@ -34,17 +33,11 @@ IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,ip");
 
 /* Type specific function prefix */
-#define TYPE		hash_ipportip
+#define HTYPE		hash_ipportip
 
-static bool
-hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_ipportip4_same_set	hash_ipportip_same_set
-#define hash_ipportip6_same_set	hash_ipportip_same_set
-
-/* The type variant functions: IPv4 */
+/* IPv4 variants */
 
-/* Member elements without timeout */
+/* Member elements  */
 struct hash_ipportip4_elem {
 	__be32 ip;
 	__be32 ip2;
@@ -53,8 +46,7 @@ struct hash_ipportip4_elem {
 	u8 padding;
 };
 
-/* Member elements with timeout support */
-struct hash_ipportip4_telem {
+struct hash_ipportip4t_elem {
 	__be32 ip;
 	__be32 ip2;
 	__be16 port;
@@ -74,25 +66,6 @@ hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipportip4_data_isnull(const struct hash_ipportip4_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_ipportip4_data_copy(struct hash_ipportip4_elem *dst,
-			 const struct hash_ipportip4_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_ipportip4_data_zero_out(struct hash_ipportip4_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_ipportip4_data_list(struct sk_buff *skb,
 		       const struct hash_ipportip4_elem *data)
@@ -108,66 +81,48 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipportip4_data_tlist(struct sk_buff *skb,
-			const struct hash_ipportip4_elem *data)
+static inline void
+hash_ipportip4_data_next(struct hash_ipportip4_elem *next,
+			 const struct hash_ipportip4_elem *d)
 {
-	const struct hash_ipportip4_telem *tdata =
-		(const struct hash_ipportip4_telem *)data;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = d->ip;
+	next->port = d->port;
 }
 
+/* Common functions */
+#define MTYPE		hash_ipportip4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ipportip4_data_next(struct ip_set_hash *h,
-			 const struct hash_ipportip4_elem *d)
-{
-	h->next.ip = d->ip;
-	h->next.port = d->port;
-}
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb,
 		    const struct xt_action_param *par,
-		    enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportip4_elem data = { };
+	struct hash_ipportip4_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
-
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 		    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportip4_elem data = { };
+	struct hash_ipportip4_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 ip, ip_to, p = 0, port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	int ret;
 
@@ -180,45 +135,40 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
-	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2);
+	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &e.ip2);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMP))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMP))
+		e.port = 0;
 
 	if (adt == IPSET_TEST ||
 	    !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] ||
 	      tb[IPSET_ATTR_PORT_TO])) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	ip_to = ip = ntohl(data.ip);
+	ip_to = ip = ntohl(e.ip);
 	if (tb[IPSET_ATTR_IP_TO]) {
 		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
 		if (ret)
@@ -233,7 +183,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 		ip_set_mask_from_to(ip, ip_to, cidr);
 	}
 
-	port_to = port = ntohs(data.port);
+	port_to = port = ntohs(e.port);
 	if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 		if (port > port_to)
@@ -246,9 +196,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
 						       : port;
 		for (; p <= port_to; p++) {
-			data.ip = htonl(ip);
-			data.port = htons(p);
-			ret = adtfn(set, &data, timeout, flags);
+			e.ip = htonl(ip);
+			e.port = htons(p);
+			ret = adtfn(set, &e, &ext, &ext, flags);
 
 			if (ret && !ip_set_eexist(ret, flags))
 				return ret;
@@ -259,18 +209,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_ipportip_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_ipportip6_elem {
 	union nf_inet_addr ip;
@@ -280,7 +219,7 @@ struct hash_ipportip6_elem {
 	u8 padding;
 };
 
-struct hash_ipportip6_telem {
+struct hash_ipportip6t_elem {
 	union nf_inet_addr ip;
 	union nf_inet_addr ip2;
 	__be16 port;
@@ -289,6 +228,8 @@ struct hash_ipportip6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
 			  const struct hash_ipportip6_elem *ip2,
@@ -300,25 +241,6 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipportip6_data_isnull(const struct hash_ipportip6_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_ipportip6_data_copy(struct hash_ipportip6_elem *dst,
-			 const struct hash_ipportip6_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_ipportip6_data_zero_out(struct hash_ipportip6_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_ipportip6_data_list(struct sk_buff *skb,
 			 const struct hash_ipportip6_elem *data)
@@ -334,68 +256,51 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipportip6_data_tlist(struct sk_buff *skb,
-			  const struct hash_ipportip6_elem *data)
+static inline void
+hash_ipportip6_data_next(struct hash_ipportip4_elem *next,
+			 const struct hash_ipportip6_elem *d)
 {
-	const struct hash_ipportip6_telem *e =
-		(const struct hash_ipportip6_telem *)data;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->port = d->port;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
 
+#define MTYPE		hash_ipportip6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ipportip6_data_next(struct ip_set_hash *h,
-			 const struct hash_ipportip6_elem *d)
-{
-	h->next.port = d->port;
-}
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb,
 		    const struct xt_action_param *par,
-		    enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportip6_elem data = { };
+	struct hash_ipportip6_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
-
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 		    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportip *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportip6_elem data = { };
+	struct hash_ipportip6_elem e = { };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	int ret;
 
@@ -410,43 +315,38 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMPV6))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+		e.port = 0;
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	port = ntohs(data.port);
+	port = ntohs(e.port);
 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 	if (port > port_to)
 		swap(port, port_to);
@@ -454,8 +354,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		port = ntohs(h->next.port);
 	for (; port <= port_to; port++) {
-		data.port = htons(port);
-		ret = adtfn(set, &data, timeout, flags);
+		e.port = htons(port);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -465,78 +365,6 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	struct ip_set_hash *h;
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_ipportip4_gc_init(set);
-		else
-			hash_ipportip6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipportip4_variant : &hash_ipportip6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_ipportip_type __read_mostly = {
 	.name		= "hash:ip,port,ip",
 	.protocol	= IPSET_PROTOCOL,
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 352f8b4..bfde4ea 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
@@ -36,23 +35,19 @@ IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:ip,port,net");
 
 /* Type specific function prefix */
-#define TYPE		hash_ipportnet
-
-static bool
-hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_ipportnet4_same_set	hash_ipportnet_same_set
-#define hash_ipportnet6_same_set	hash_ipportnet_same_set
-
-/* The type variant functions: IPv4 */
+#define HTYPE		hash_ipportnet
 
 /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
  * However this way we have to store internally cidr - 1,
  * dancing back and forth.
  */
 #define IP_SET_HASH_WITH_NETS_PACKED
+#define IP_SET_HASH_WITH_PROTO
+#define IP_SET_HASH_WITH_NETS
 
-/* Member elements without timeout */
+/* IPv4 variants */
+
+/* Member elements */
 struct hash_ipportnet4_elem {
 	__be32 ip;
 	__be32 ip2;
@@ -62,8 +57,7 @@ struct hash_ipportnet4_elem {
 	u8 proto;
 };
 
-/* Member elements with timeout support */
-struct hash_ipportnet4_telem {
+struct hash_ipportnet4t_elem {
 	__be32 ip;
 	__be32 ip2;
 	__be16 port;
@@ -73,6 +67,8 @@ struct hash_ipportnet4_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
 			   const struct hash_ipportnet4_elem *ip2,
@@ -85,38 +81,22 @@ hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem)
+static inline int
+hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem)
 {
-	return elem->proto == 0;
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst,
-			  const struct hash_ipportnet4_elem *src)
+hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags)
 {
-	memcpy(dst, src, sizeof(*dst));
+	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
 }
 
 static inline void
-hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags)
+hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
-}
-
-static inline void
-hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem)
-{
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -126,12 +106,6 @@ hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr)
 	elem->cidr = cidr - 1;
 }
 
-static inline void
-hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_ipportnet4_data_list(struct sk_buff *skb,
 			  const struct hash_ipportnet4_elem *data)
@@ -152,81 +126,56 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipportnet4_data_tlist(struct sk_buff *skb,
-			   const struct hash_ipportnet4_elem *data)
+static inline void
+hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next,
+			  const struct hash_ipportnet4_elem *d)
 {
-	const struct hash_ipportnet4_telem *tdata =
-		(const struct hash_ipportnet4_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = d->ip;
+	next->port = d->port;
+	next->ip2 = d->ip2;
 }
 
-#define IP_SET_HASH_WITH_PROTO
-#define IP_SET_HASH_WITH_NETS
-
+#define MTYPE		hash_ipportnet4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ipportnet4_data_next(struct ip_set_hash *h,
-			  const struct hash_ipportnet4_elem *d)
-{
-	h->next.ip = d->ip;
-	h->next.port = d->port;
-	h->next.ip2 = d->ip2;
-}
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
 		     const struct xt_action_param *par,
-		     enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportnet *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportnet4_elem data = {
+	struct hash_ipportnet4_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK - 1;
+		e.cidr = HOST_MASK - 1;
 
 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2);
-	data.ip2 &= ip_set_netmask(data.cidr + 1);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2);
+	e.ip2 &= ip_set_netmask(e.cidr + 1);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportnet *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 };
+	struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 ip, ip_to, p = 0, port, port_to;
 	u32 ip2_from, ip2_to, ip2_last, ip2;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	u8 cidr;
 	int ret;
@@ -241,7 +190,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
@@ -253,31 +203,25 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
 		if (!cidr || cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
-		data.cidr = cidr - 1;
+		e.cidr = cidr - 1;
 	}
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMP))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMP))
+		e.port = 0;
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -289,9 +233,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (adt == IPSET_TEST ||
 	    !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports ||
 	      tb[IPSET_ATTR_IP2_TO])) {
-		data.ip = htonl(ip);
-		data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr + 1));
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip);
+		e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1));
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
@@ -311,7 +255,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		ip_set_mask_from_to(ip, ip_to, cidr);
 	}
 
-	port_to = port = ntohs(data.port);
+	port_to = port = ntohs(e.port);
 	if (tb[IPSET_ATTR_PORT_TO]) {
 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 		if (port > port_to)
@@ -327,28 +271,27 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 			swap(ip2_from, ip2_to);
 		if (ip2_from + UINT_MAX == ip2_to)
 			return -IPSET_ERR_HASH_RANGE;
-	} else {
-		ip_set_mask_from_to(ip2_from, ip2_to, data.cidr + 1);
-	}
+	} else
+		ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
 
 	if (retried)
 		ip = ntohl(h->next.ip);
 	for (; !before(ip_to, ip); ip++) {
-		data.ip = htonl(ip);
+		e.ip = htonl(ip);
 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
 						       : port;
 		for (; p <= port_to; p++) {
-			data.port = htons(p);
+			e.port = htons(p);
 			ip2 = retried
 			      && ip == ntohl(h->next.ip)
 			      && p == ntohs(h->next.port)
 				? ntohl(h->next.ip2) : ip2_from;
 			while (!after(ip2, ip2_to)) {
-				data.ip2 = htonl(ip2);
+				e.ip2 = htonl(ip2);
 				ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
 								&cidr);
-				data.cidr = cidr - 1;
-				ret = adtfn(set, &data, timeout, flags);
+				e.cidr = cidr - 1;
+				ret = adtfn(set, &e, &ext, &ext, flags);
 
 				if (ret && !ip_set_eexist(ret, flags))
 					return ret;
@@ -361,18 +304,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_ipportnet6_elem {
 	union nf_inet_addr ip;
@@ -383,7 +315,7 @@ struct hash_ipportnet6_elem {
 	u8 proto;
 };
 
-struct hash_ipportnet6_telem {
+struct hash_ipportnet6t_elem {
 	union nf_inet_addr ip;
 	union nf_inet_addr ip2;
 	__be16 port;
@@ -393,6 +325,8 @@ struct hash_ipportnet6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
 			   const struct hash_ipportnet6_elem *ip2,
@@ -405,44 +339,22 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
 	       ip1->proto == ip2->proto;
 }
 
-static inline bool
-hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst,
-			  const struct hash_ipportnet6_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags)
+static inline int
+hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem)
+hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags)
 {
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
 }
 
 static inline void
-hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem)
+hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags)
 {
-	elem->proto = 0;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -472,78 +384,58 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_ipportnet6_data_tlist(struct sk_buff *skb,
-			   const struct hash_ipportnet6_elem *data)
+static inline void
+hash_ipportnet6_data_next(struct hash_ipportnet4_elem *next,
+			  const struct hash_ipportnet6_elem *d)
 {
-	const struct hash_ipportnet6_telem *e =
-		(const struct hash_ipportnet6_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->port = d->port;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
 
+#define MTYPE		hash_ipportnet6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_ipportnet6_data_next(struct ip_set_hash *h,
-			  const struct hash_ipportnet6_elem *d)
-{
-	h->next.port = d->port;
-}
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
 		     const struct xt_action_param *par,
-		     enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		     enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportnet *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportnet6_elem data = {
+	struct hash_ipportnet6_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK - 1;
+		e.cidr = HOST_MASK - 1;
 
 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6);
-	ip6_netmask(&data.ip2, data.cidr + 1);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6);
+	ip6_netmask(&e.ip2, e.cidr + 1);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 		     enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_ipportnet *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_ipportnet6_elem data = { .cidr = HOST_MASK - 1 };
+	struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	u8 cidr;
 	int ret;
@@ -562,11 +454,12 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2);
 	if (ret)
 		return ret;
 
@@ -574,33 +467,27 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]);
 		if (!cidr || cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
-		data.cidr = cidr - 1;
+		e.cidr = cidr - 1;
 	}
 
-	ip6_netmask(&data.ip2, data.cidr + 1);
+	ip6_netmask(&e.ip2, e.cidr + 1);
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMPV6))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+		e.port = 0;
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -609,12 +496,12 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 	}
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	port = ntohs(data.port);
+	port = ntohs(e.port);
 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 	if (port > port_to)
 		swap(port, port_to);
@@ -622,8 +509,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		port = ntohs(h->next.port);
 	for (; port <= port_to; port++) {
-		data.port = htons(port);
-		ret = adtfn(set, &data, timeout, flags);
+		e.port = htons(port);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -633,81 +520,6 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	struct ip_set_hash *h;
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h)
-		    + sizeof(struct ip_set_hash_nets)
-		      * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipportnet4_tvariant
-			: &hash_ipportnet6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_ipportnet4_gc_init(set);
-		else
-			hash_ipportnet6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_ipportnet4_variant : &hash_ipportnet6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_ipportnet_type __read_mostly = {
 	.name		= "hash:ip,port,net",
 	.protocol	= IPSET_PROTOCOL,
@@ -724,6 +536,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
 		[IPSET_ATTR_PROBES]	= { .type = NLA_U8 },
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 370947c..cfbcdd4 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -20,7 +20,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
@@ -33,17 +32,12 @@ IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:net");
 
 /* Type specific function prefix */
-#define TYPE		hash_net
-
-static bool
-hash_net_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_net4_same_set	hash_net_same_set
-#define hash_net6_same_set	hash_net_same_set
+#define HTYPE		hash_net
+#define IP_SET_HASH_WITH_NETS
 
-/* The type variant functions: IPv4 */
+/* IPv4 variants */
 
-/* Member elements without timeout */
+/* Member elements  */
 struct hash_net4_elem {
 	__be32 ip;
 	u16 padding0;
@@ -51,8 +45,7 @@ struct hash_net4_elem {
 	u8 cidr;
 };
 
-/* Member elements with timeout support */
-struct hash_net4_telem {
+struct hash_net4t_elem {
 	__be32 ip;
 	u16 padding0;
 	u8 nomatch;
@@ -60,6 +53,8 @@ struct hash_net4_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_net4_data_equal(const struct hash_net4_elem *ip1,
 		     const struct hash_net4_elem *ip2,
@@ -69,40 +64,22 @@ hash_net4_data_equal(const struct hash_net4_elem *ip1,
 	       ip1->cidr == ip2->cidr;
 }
 
-static inline bool
-hash_net4_data_isnull(const struct hash_net4_elem *elem)
-{
-	return elem->cidr == 0;
-}
-
-static inline void
-hash_net4_data_copy(struct hash_net4_elem *dst,
-		    const struct hash_net4_elem *src)
+static inline int
+hash_net4_do_data_match(const struct hash_net4_elem *elem)
 {
-	dst->ip = src->ip;
-	dst->cidr = src->cidr;
-	dst->nomatch = src->nomatch;
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags)
+hash_net4_data_set_flags(struct hash_net4_elem *elem, u32 flags)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+	elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
 }
 
 static inline void
-hash_net4_data_reset_flags(struct hash_net4_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_net4_data_match(const struct hash_net4_elem *elem)
+hash_net4_data_reset_flags(struct hash_net4_elem *elem, u8 *flags)
 {
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -112,13 +89,6 @@ hash_net4_data_netmask(struct hash_net4_elem *elem, u8 cidr)
 	elem->cidr = cidr;
 }
 
-/* Zero CIDR values cannot be stored */
-static inline void
-hash_net4_data_zero_out(struct hash_net4_elem *elem)
-{
-	elem->cidr = 0;
-}
-
 static bool
 hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data)
 {
@@ -135,69 +105,49 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data)
+static inline void
+hash_net4_data_next(struct hash_net4_elem *next,
+		    const struct hash_net4_elem *d)
 {
-	const struct hash_net4_telem *tdata =
-		(const struct hash_net4_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, tdata->cidr) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = d->ip;
 }
 
-#define IP_SET_HASH_WITH_NETS
-
+#define MTYPE		hash_net4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_net4_data_next(struct ip_set_hash *h,
-		    const struct hash_net4_elem *d)
-{
-	h->next.ip = d->ip;
-}
+#include "ip_set_hash_gen.h"
 
 static int
 hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
 	       const struct xt_action_param *par,
-	       enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	       enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_net *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_net4_elem data = {
+	struct hash_net4_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
-	if (data.cidr == 0)
+	if (e.cidr == 0)
 		return -EINVAL;
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK;
+		e.cidr = HOST_MASK;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-	data.ip &= ip_set_netmask(data.cidr);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	e.ip &= ip_set_netmask(e.cidr);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 	       enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_net *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_net4_elem data = { .cidr = HOST_MASK };
-	u32 timeout = h->timeout;
+	struct hash_net4_elem e = { .cidr = HOST_MASK };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 ip = 0, ip_to, last;
 	int ret;
 
@@ -209,22 +159,17 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_CIDR]) {
-		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-		if (!data.cidr || data.cidr > HOST_MASK)
+		e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+		if (!e.cidr || e.cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
 	}
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
-
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
@@ -232,8 +177,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 	}
 
 	if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
-		data.ip = htonl(ip & ip_set_hostmask(data.cidr));
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip & ip_set_hostmask(e.cidr));
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
@@ -251,9 +196,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		ip = ntohl(h->next.ip);
 	while (!after(ip, ip_to)) {
-		data.ip = htonl(ip);
-		last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip);
+		last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
 		else
@@ -263,18 +208,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_net6_elem {
 	union nf_inet_addr ip;
@@ -283,7 +217,7 @@ struct hash_net6_elem {
 	u8 cidr;
 };
 
-struct hash_net6_telem {
+struct hash_net6t_elem {
 	union nf_inet_addr ip;
 	u16 padding0;
 	u8 nomatch;
@@ -291,6 +225,8 @@ struct hash_net6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_net6_data_equal(const struct hash_net6_elem *ip1,
 		     const struct hash_net6_elem *ip2,
@@ -300,46 +236,22 @@ hash_net6_data_equal(const struct hash_net6_elem *ip1,
 	       ip1->cidr == ip2->cidr;
 }
 
-static inline bool
-hash_net6_data_isnull(const struct hash_net6_elem *elem)
-{
-	return elem->cidr == 0;
-}
-
-static inline void
-hash_net6_data_copy(struct hash_net6_elem *dst,
-		    const struct hash_net6_elem *src)
-{
-	dst->ip.in6 = src->ip.in6;
-	dst->cidr = src->cidr;
-	dst->nomatch = src->nomatch;
-}
-
-static inline void
-hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags)
+static inline int
+hash_net6_do_data_match(const struct hash_net6_elem *elem)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_net6_data_reset_flags(struct hash_net6_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_net6_data_match(const struct hash_net6_elem *elem)
+hash_net6_data_set_flags(struct hash_net6_elem *elem, u32 flags)
 {
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
 }
 
 static inline void
-hash_net6_data_zero_out(struct hash_net6_elem *elem)
+hash_net6_data_reset_flags(struct hash_net6_elem *elem, u8 *flags)
 {
-	elem->cidr = 0;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -365,69 +277,53 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data)
+static inline void
+hash_net6_data_next(struct hash_net4_elem *next,
+		    const struct hash_net6_elem *d)
 {
-	const struct hash_net6_telem *e =
-		(const struct hash_net6_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, e->cidr) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
 
+#define MTYPE		hash_net6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_net6_data_next(struct ip_set_hash *h,
-		    const struct hash_net6_elem *d)
-{
-}
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
 	       const struct xt_action_param *par,
-	       enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	       enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_net *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_net6_elem data = {
+	struct hash_net6_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
-	if (data.cidr == 0)
+	if (e.cidr == 0)
 		return -EINVAL;
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK;
+		e.cidr = HOST_MASK;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-	ip6_netmask(&data.ip, data.cidr);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	ip6_netmask(&e.ip, e.cidr);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 	       enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_net *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_net6_elem data = { .cidr = HOST_MASK };
-	u32 timeout = h->timeout;
+	struct hash_net6_elem e = { .cidr = HOST_MASK };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	int ret;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
@@ -440,23 +336,18 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_CIDR])
-		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+		e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
-	if (!data.cidr || data.cidr > HOST_MASK)
+	if (!e.cidr || e.cidr > HOST_MASK)
 		return -IPSET_ERR_INVALID_CIDR;
 
-	ip6_netmask(&data.ip, data.cidr);
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	ip6_netmask(&e.ip, e.cidr);
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -464,86 +355,12 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
-	ret = adtfn(set, &data, timeout, flags);
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 	return ip_set_enomatch(ret, flags, adt) ? 1 :
 	       ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	struct ip_set_hash *h;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h)
-		    + sizeof(struct ip_set_hash_nets)
-		      * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_net4_tvariant : &hash_net6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_net4_gc_init(set);
-		else
-			hash_net6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_net4_variant : &hash_net6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_net_type __read_mostly = {
 	.name		= "hash:net",
 	.protocol	= IPSET_PROTOCOL,
@@ -559,6 +376,7 @@ static struct ip_set_type hash_net_type __read_mostly = {
 		[IPSET_ATTR_PROBES]	= { .type = NLA_U8 },
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index dd9843e..555ebb7 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2011-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
@@ -127,17 +126,14 @@ iface_add(struct rb_root *root, const char **iface)
 }
 
 /* Type specific function prefix */
-#define TYPE		hash_netiface
-
-static bool
-hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_netiface4_same_set	hash_netiface_same_set
-#define hash_netiface6_same_set	hash_netiface_same_set
+#define HTYPE		hash_netiface
+#define IP_SET_HASH_WITH_NETS
+#define IP_SET_HASH_WITH_RBTREE
+#define IP_SET_HASH_WITH_MULTI
 
 #define STREQ(a, b)	(strcmp(a, b) == 0)
 
-/* The type variant functions: IPv4 */
+/* IPv4 variants */
 
 struct hash_netiface4_elem_hashed {
 	__be32 ip;
@@ -147,8 +143,6 @@ struct hash_netiface4_elem_hashed {
 	u8 elem;
 };
 
-#define HKEY_DATALEN	sizeof(struct hash_netiface4_elem_hashed)
-
 /* Member elements without timeout */
 struct hash_netiface4_elem {
 	__be32 ip;
@@ -159,8 +153,7 @@ struct hash_netiface4_elem {
 	const char *iface;
 };
 
-/* Member elements with timeout support */
-struct hash_netiface4_telem {
+struct hash_netiface4t_elem {
 	__be32 ip;
 	u8 physdev;
 	u8 cidr;
@@ -170,6 +163,8 @@ struct hash_netiface4_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
 			  const struct hash_netiface4_elem *ip2,
@@ -182,38 +177,22 @@ hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
 	       ip1->iface == ip2->iface;
 }
 
-static inline bool
-hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem)
+static inline int
+hash_netiface4_do_data_match(const struct hash_netiface4_elem *elem)
 {
-	return elem->elem == 0;
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
-			 const struct hash_netiface4_elem *src)
+hash_netiface4_data_set_flags(struct hash_netiface4_elem *elem, u32 flags)
 {
-	memcpy(dst, src, sizeof(*dst));
+	elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
 }
 
 static inline void
-hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags)
+hash_netiface4_data_reset_flags(struct hash_netiface4_elem *elem, u8 *flags)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
-}
-
-static inline void
-hash_netiface4_data_reset_flags(struct hash_netiface4_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_netiface4_data_match(const struct hash_netiface4_elem *elem)
-{
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -223,12 +202,6 @@ hash_netiface4_data_netmask(struct hash_netiface4_elem *elem, u8 cidr)
 	elem->cidr = cidr;
 }
 
-static inline void
-hash_netiface4_data_zero_out(struct hash_netiface4_elem *elem)
-{
-	elem->elem = 0;
-}
-
 static bool
 hash_netiface4_data_list(struct sk_buff *skb,
 			 const struct hash_netiface4_elem *data)
@@ -249,66 +222,40 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_netiface4_data_tlist(struct sk_buff *skb,
-			  const struct hash_netiface4_elem *data)
+static inline void
+hash_netiface4_data_next(struct hash_netiface4_elem *next,
+			 const struct hash_netiface4_elem *d)
 {
-	const struct hash_netiface4_telem *tdata =
-		(const struct hash_netiface4_telem *)data;
-	u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
-
-	if (data->nomatch)
-		flags |= IPSET_FLAG_NOMATCH;
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) ||
-	    nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))))
-		goto nla_put_failure;
-
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = d->ip;
 }
 
-#define IP_SET_HASH_WITH_NETS
-#define IP_SET_HASH_WITH_RBTREE
-#define IP_SET_HASH_WITH_MULTI
-
+#define MTYPE		hash_netiface4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_netiface4_data_next(struct ip_set_hash *h,
-			 const struct hash_netiface4_elem *d)
-{
-	h->next.ip = d->ip;
-}
+#define HKEY_DATALEN	sizeof(struct hash_netiface4_elem_hashed)
+#include "ip_set_hash_gen.h"
 
 static int
 hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
 		    const struct xt_action_param *par,
-		    enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	struct ip_set_hash *h = set->data;
+	struct hash_netiface *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netiface4_elem data = {
+	struct hash_netiface4_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
 		.elem = 1,
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 	int ret;
 
-	if (data.cidr == 0)
+	if (e.cidr == 0)
 		return -EINVAL;
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK;
+		e.cidr = HOST_MASK;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-	data.ip &= ip_set_netmask(data.cidr);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	e.ip &= ip_set_netmask(e.cidr);
 
 #define IFACE(dir)	(par->dir ? par->dir->name : NULL)
 #define PHYSDEV(dir)	(nf_bridge->dir ? nf_bridge->dir->name : NULL)
@@ -320,38 +267,38 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
 
 		if (!nf_bridge)
 			return -EINVAL;
-		data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
-		data.physdev = 1;
+		e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
+		e.physdev = 1;
 #else
-		data.iface = NULL;
+		e.iface = NULL;
 #endif
 	} else
-		data.iface = SRCDIR ? IFACE(in) : IFACE(out);
+		e.iface = SRCDIR ? IFACE(in) : IFACE(out);
 
-	if (!data.iface)
+	if (!e.iface)
 		return -EINVAL;
-	ret = iface_test(&h->rbtree, &data.iface);
+	ret = iface_test(&h->rbtree, &e.iface);
 	if (adt == IPSET_ADD) {
 		if (!ret) {
-			ret = iface_add(&h->rbtree, &data.iface);
+			ret = iface_add(&h->rbtree, &e.iface);
 			if (ret)
 				return ret;
 		}
 	} else if (!ret)
 		return ret;
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 		    enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	struct ip_set_hash *h = set->data;
+	struct hash_netiface *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netiface4_elem data = { .cidr = HOST_MASK, .elem = 1 };
+	struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 ip = 0, ip_to, last;
-	u32 timeout = h->timeout;
 	char iface[IFNAMSIZ];
 	int ret;
 
@@ -364,28 +311,23 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_CIDR]) {
-		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-		if (data.cidr > HOST_MASK)
+		e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+		if (e.cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
 	}
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
-
 	strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
-	data.iface = iface;
-	ret = iface_test(&h->rbtree, &data.iface);
+	e.iface = iface;
+	ret = iface_test(&h->rbtree, &e.iface);
 	if (adt == IPSET_ADD) {
 		if (!ret) {
-			ret = iface_add(&h->rbtree, &data.iface);
+			ret = iface_add(&h->rbtree, &e.iface);
 			if (ret)
 				return ret;
 		}
@@ -395,13 +337,13 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_PHYSDEV)
-			data.physdev = 1;
+			e.physdev = 1;
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
 			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 	if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
-		data.ip = htonl(ip & ip_set_hostmask(data.cidr));
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip & ip_set_hostmask(e.cidr));
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
@@ -414,16 +356,15 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 			swap(ip, ip_to);
 		if (ip + UINT_MAX == ip_to)
 			return -IPSET_ERR_HASH_RANGE;
-	} else {
-		ip_set_mask_from_to(ip, ip_to, data.cidr);
-	}
+	} else
+		ip_set_mask_from_to(ip, ip_to, e.cidr);
 
 	if (retried)
 		ip = ntohl(h->next.ip);
 	while (!after(ip, ip_to)) {
-		data.ip = htonl(ip);
-		last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip);
+		last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -434,18 +375,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_netiface6_elem_hashed {
 	union nf_inet_addr ip;
@@ -455,8 +385,6 @@ struct hash_netiface6_elem_hashed {
 	u8 elem;
 };
 
-#define HKEY_DATALEN	sizeof(struct hash_netiface6_elem_hashed)
-
 struct hash_netiface6_elem {
 	union nf_inet_addr ip;
 	u8 physdev;
@@ -466,7 +394,7 @@ struct hash_netiface6_elem {
 	const char *iface;
 };
 
-struct hash_netiface6_telem {
+struct hash_netiface6t_elem {
 	union nf_inet_addr ip;
 	u8 physdev;
 	u8 cidr;
@@ -476,6 +404,8 @@ struct hash_netiface6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
 			  const struct hash_netiface6_elem *ip2,
@@ -488,44 +418,22 @@ hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
 	       ip1->iface == ip2->iface;
 }
 
-static inline bool
-hash_netiface6_data_isnull(const struct hash_netiface6_elem *elem)
-{
-	return elem->elem == 0;
-}
-
-static inline void
-hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
-			 const struct hash_netiface6_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags)
-{
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
-}
-
 static inline int
-hash_netiface6_data_match(const struct hash_netiface6_elem *elem)
+hash_netiface6_do_data_match(const struct hash_netiface6_elem *elem)
 {
 	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_netiface6_data_reset_flags(struct hash_netiface6_elem *dst, u32 *flags)
+hash_netiface6_data_set_flags(struct hash_netiface6_elem *elem, u32 flags)
 {
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
+	elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH;
 }
 
 static inline void
-hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
+hash_netiface6_data_reset_flags(struct hash_netiface6_elem *elem, u8 *flags)
 {
-	elem->elem = 0;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -555,63 +463,45 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_netiface6_data_tlist(struct sk_buff *skb,
-			  const struct hash_netiface6_elem *data)
+static inline void
+hash_netiface6_data_next(struct hash_netiface4_elem *next,
+			 const struct hash_netiface6_elem *d)
 {
-	const struct hash_netiface6_telem *e =
-		(const struct hash_netiface6_telem *)data;
-	u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0;
-
-	if (data->nomatch)
-		flags |= IPSET_FLAG_NOMATCH;
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) ||
-	    nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
+#undef HKEY_DATALEN
 
+#define MTYPE		hash_netiface6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_netiface6_data_next(struct ip_set_hash *h,
-			 const struct hash_netiface6_elem *d)
-{
-}
+#define HKEY_DATALEN	sizeof(struct hash_netiface6_elem_hashed)
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
 		    const struct xt_action_param *par,
-		    enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		    enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	struct ip_set_hash *h = set->data;
+	struct hash_netiface *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netiface6_elem data = {
+	struct hash_netiface6_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK,
 		.elem = 1,
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 	int ret;
 
-	if (data.cidr == 0)
+	if (e.cidr == 0)
 		return -EINVAL;
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK;
+		e.cidr = HOST_MASK;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-	ip6_netmask(&data.ip, data.cidr);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	ip6_netmask(&e.ip, e.cidr);
 
 	if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
 #ifdef CONFIG_BRIDGE_NETFILTER
@@ -619,37 +509,37 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
 
 		if (!nf_bridge)
 			return -EINVAL;
-		data.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
-		data.physdev = 1;
+		e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
+		e.physdev = 1;
 #else
-		data.iface = NULL;
+		e.iface = NULL;
 #endif
 	} else
-		data.iface = SRCDIR ? IFACE(in) : IFACE(out);
+		e.iface = SRCDIR ? IFACE(in) : IFACE(out);
 
-	if (!data.iface)
+	if (!e.iface)
 		return -EINVAL;
-	ret = iface_test(&h->rbtree, &data.iface);
+	ret = iface_test(&h->rbtree, &e.iface);
 	if (adt == IPSET_ADD) {
 		if (!ret) {
-			ret = iface_add(&h->rbtree, &data.iface);
+			ret = iface_add(&h->rbtree, &e.iface);
 			if (ret)
 				return ret;
 		}
 	} else if (!ret)
 		return ret;
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 		   enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	struct ip_set_hash *h = set->data;
+	struct hash_netiface *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netiface6_elem data = { .cidr = HOST_MASK, .elem = 1 };
-	u32 timeout = h->timeout;
+	struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	char iface[IFNAMSIZ];
 	int ret;
 
@@ -664,28 +554,23 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
 	if (tb[IPSET_ATTR_CIDR])
-		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
-	if (data.cidr > HOST_MASK)
+		e.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
+	if (e.cidr > HOST_MASK)
 		return -IPSET_ERR_INVALID_CIDR;
-	ip6_netmask(&data.ip, data.cidr);
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	ip6_netmask(&e.ip, e.cidr);
 
 	strcpy(iface, nla_data(tb[IPSET_ATTR_IFACE]));
-	data.iface = iface;
-	ret = iface_test(&h->rbtree, &data.iface);
+	e.iface = iface;
+	ret = iface_test(&h->rbtree, &e.iface);
 	if (adt == IPSET_ADD) {
 		if (!ret) {
-			ret = iface_add(&h->rbtree, &data.iface);
+			ret = iface_add(&h->rbtree, &e.iface);
 			if (ret)
 				return ret;
 		}
@@ -695,93 +580,17 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 		if (cadt_flags & IPSET_FLAG_PHYSDEV)
-			data.physdev = 1;
+			e.physdev = 1;
 		if (cadt_flags & IPSET_FLAG_NOMATCH)
 			flags |= (IPSET_FLAG_NOMATCH << 16);
 	}
 
-	ret = adtfn(set, &data, timeout, flags);
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 	return ip_set_enomatch(ret, flags, adt) ? 1 :
 	       ip_set_eexist(ret, flags) ? 0 : ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	struct ip_set_hash *h;
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h)
-		    + sizeof(struct ip_set_hash_nets)
-		      * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-	h->ahash_max = AHASH_MAX_SIZE;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-	h->rbtree = RB_ROOT;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_netiface4_tvariant : &hash_netiface6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_netiface4_gc_init(set);
-		else
-			hash_netiface6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_netiface4_variant : &hash_netiface6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_netiface_type __read_mostly = {
 	.name		= "hash:net,iface",
 	.protocol	= IPSET_PROTOCOL,
@@ -799,6 +608,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index cd1d3c1..43b1f1e 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -20,7 +20,6 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_getport.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
@@ -35,15 +34,9 @@ IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_hash:net,port");
 
 /* Type specific function prefix */
-#define TYPE		hash_netport
-
-static bool
-hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
-
-#define hash_netport4_same_set	hash_netport_same_set
-#define hash_netport6_same_set	hash_netport_same_set
-
-/* The type variant functions: IPv4 */
+#define HTYPE		hash_netport
+#define IP_SET_HASH_WITH_PROTO
+#define IP_SET_HASH_WITH_NETS
 
 /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0
  * However this way we have to store internally cidr - 1,
@@ -51,7 +44,9 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b);
  */
 #define IP_SET_HASH_WITH_NETS_PACKED
 
-/* Member elements without timeout */
+/* IPv4 variants */
+
+/* Member elements */
 struct hash_netport4_elem {
 	__be32 ip;
 	__be16 port;
@@ -60,8 +55,7 @@ struct hash_netport4_elem {
 	u8 nomatch:1;
 };
 
-/* Member elements with timeout support */
-struct hash_netport4_telem {
+struct hash_netport4t_elem {
 	__be32 ip;
 	__be16 port;
 	u8 proto;
@@ -70,6 +64,8 @@ struct hash_netport4_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
 			 const struct hash_netport4_elem *ip2,
@@ -81,42 +77,22 @@ hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
 	       ip1->cidr == ip2->cidr;
 }
 
-static inline bool
-hash_netport4_data_isnull(const struct hash_netport4_elem *elem)
+static inline int
+hash_netport4_do_data_match(const struct hash_netport4_elem *elem)
 {
-	return elem->proto == 0;
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_netport4_data_copy(struct hash_netport4_elem *dst,
-			const struct hash_netport4_elem *src)
+hash_netport4_data_set_flags(struct hash_netport4_elem *elem, u32 flags)
 {
-	dst->ip = src->ip;
-	dst->port = src->port;
-	dst->proto = src->proto;
-	dst->cidr = src->cidr;
-	dst->nomatch = src->nomatch;
+	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
 }
 
 static inline void
-hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
+hash_netport4_data_reset_flags(struct hash_netport4_elem *elem, u8 *flags)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
-}
-
-static inline void
-hash_netport4_data_reset_flags(struct hash_netport4_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_netport4_data_match(const struct hash_netport4_elem *elem)
-{
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -126,12 +102,6 @@ hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr)
 	elem->cidr = cidr - 1;
 }
 
-static inline void
-hash_netport4_data_zero_out(struct hash_netport4_elem *elem)
-{
-	elem->proto = 0;
-}
-
 static bool
 hash_netport4_data_list(struct sk_buff *skb,
 			const struct hash_netport4_elem *data)
@@ -151,77 +121,53 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_netport4_data_tlist(struct sk_buff *skb,
-			 const struct hash_netport4_elem *data)
+static inline void
+hash_netport4_data_next(struct hash_netport4_elem *next,
+			const struct hash_netport4_elem *d)
 {
-	const struct hash_netport4_telem *tdata =
-		(const struct hash_netport4_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(tdata->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->ip = d->ip;
+	next->port = d->port;
 }
 
-#define IP_SET_HASH_WITH_PROTO
-#define IP_SET_HASH_WITH_NETS
-
+#define MTYPE		hash_netport4
 #define PF		4
 #define HOST_MASK	32
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_netport4_data_next(struct ip_set_hash *h,
-			const struct hash_netport4_elem *d)
-{
-	h->next.ip = d->ip;
-	h->next.port = d->port;
-}
+#include "ip_set_hash_gen.h"
 
 static int
 hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
 		   const struct xt_action_param *par,
-		   enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		   enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_netport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netport4_elem data = {
+	struct hash_netport4_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK - 1;
+		e.cidr = HOST_MASK - 1;
 
 	if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
-	data.ip &= ip_set_netmask(data.cidr + 1);
+	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
+	e.ip &= ip_set_netmask(e.cidr + 1);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		   enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_netport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 };
+	struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 port, port_to, p = 0, ip = 0, ip_to, last;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	u8 cidr;
 	int ret;
@@ -236,7 +182,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
+	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
@@ -244,31 +191,25 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 		if (!cidr || cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
-		data.cidr = cidr - 1;
+		e.cidr = cidr - 1;
 	}
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMP))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMP))
+		e.port = 0;
 
 	with_ports = with_ports && tb[IPSET_ATTR_PORT_TO];
 
@@ -279,13 +220,13 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	}
 
 	if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) {
-		data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1));
-		ret = adtfn(set, &data, timeout, flags);
+		e.ip = htonl(ip & ip_set_hostmask(e.cidr + 1));
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	port = port_to = ntohs(data.port);
+	port = port_to = ntohs(e.port);
 	if (tb[IPSET_ATTR_PORT_TO]) {
 		port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 		if (port_to < port)
@@ -299,21 +240,20 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 			swap(ip, ip_to);
 		if (ip + UINT_MAX == ip_to)
 			return -IPSET_ERR_HASH_RANGE;
-	} else {
-		ip_set_mask_from_to(ip, ip_to, data.cidr + 1);
-	}
+	} else
+		ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
 
 	if (retried)
 		ip = ntohl(h->next.ip);
 	while (!after(ip, ip_to)) {
-		data.ip = htonl(ip);
+		e.ip = htonl(ip);
 		last = ip_set_range_to_cidr(ip, ip_to, &cidr);
-		data.cidr = cidr - 1;
+		e.cidr = cidr - 1;
 		p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
 						       : port;
 		for (; p <= port_to; p++) {
-			data.port = htons(p);
-			ret = adtfn(set, &data, timeout, flags);
+			e.port = htons(p);
+			ret = adtfn(set, &e, &ext, &ext, flags);
 
 			if (ret && !ip_set_eexist(ret, flags))
 				return ret;
@@ -325,18 +265,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-static bool
-hash_netport_same_set(const struct ip_set *a, const struct ip_set *b)
-{
-	const struct ip_set_hash *x = a->data;
-	const struct ip_set_hash *y = b->data;
-
-	/* Resizing changes htable_bits, so we ignore it */
-	return x->maxelem == y->maxelem &&
-	       x->timeout == y->timeout;
-}
-
-/* The type variant functions: IPv6 */
+/* IPv6 variants */
 
 struct hash_netport6_elem {
 	union nf_inet_addr ip;
@@ -346,7 +275,7 @@ struct hash_netport6_elem {
 	u8 nomatch:1;
 };
 
-struct hash_netport6_telem {
+struct hash_netport6t_elem {
 	union nf_inet_addr ip;
 	__be16 port;
 	u8 proto;
@@ -355,6 +284,8 @@ struct hash_netport6_telem {
 	unsigned long timeout;
 };
 
+/* Common functions */
+
 static inline bool
 hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
 			 const struct hash_netport6_elem *ip2,
@@ -366,44 +297,22 @@ hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
 	       ip1->cidr == ip2->cidr;
 }
 
-static inline bool
-hash_netport6_data_isnull(const struct hash_netport6_elem *elem)
-{
-	return elem->proto == 0;
-}
-
-static inline void
-hash_netport6_data_copy(struct hash_netport6_elem *dst,
-			const struct hash_netport6_elem *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-static inline void
-hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
+static inline int
+hash_netport6_do_data_match(const struct hash_netport6_elem *elem)
 {
-	dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
+	return elem->nomatch ? -ENOTEMPTY : 1;
 }
 
 static inline void
-hash_netport6_data_reset_flags(struct hash_netport6_elem *dst, u32 *flags)
-{
-	if (dst->nomatch) {
-		*flags = IPSET_FLAG_NOMATCH;
-		dst->nomatch = 0;
-	}
-}
-
-static inline int
-hash_netport6_data_match(const struct hash_netport6_elem *elem)
+hash_netport6_data_set_flags(struct hash_netport6_elem *elem, u32 flags)
 {
-	return elem->nomatch ? -ENOTEMPTY : 1;
+	elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH);
 }
 
 static inline void
-hash_netport6_data_zero_out(struct hash_netport6_elem *elem)
+hash_netport6_data_reset_flags(struct hash_netport6_elem *elem, u8 *flags)
 {
-	elem->proto = 0;
+	swap(*flags, elem->nomatch);
 }
 
 static inline void
@@ -432,76 +341,57 @@ nla_put_failure:
 	return 1;
 }
 
-static bool
-hash_netport6_data_tlist(struct sk_buff *skb,
-			 const struct hash_netport6_elem *data)
+static inline void
+hash_netport6_data_next(struct hash_netport4_elem *next,
+			const struct hash_netport6_elem *d)
 {
-	const struct hash_netport6_telem *e =
-		(const struct hash_netport6_telem *)data;
-	u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0;
-
-	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) ||
-	    nla_put_net16(skb, IPSET_ATTR_PORT, data->port) ||
-	    nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) ||
-	    nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) ||
-	    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
-			  htonl(ip_set_timeout_get(e->timeout))) ||
-	    (flags &&
-	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))))
-		goto nla_put_failure;
-	return 0;
-
-nla_put_failure:
-	return 1;
+	next->port = d->port;
 }
 
+#undef MTYPE
 #undef PF
 #undef HOST_MASK
 
+#define MTYPE		hash_netport6
 #define PF		6
 #define HOST_MASK	128
-#include <linux/netfilter/ipset/ip_set_ahash.h>
-
-static inline void
-hash_netport6_data_next(struct ip_set_hash *h,
-			const struct hash_netport6_elem *d)
-{
-	h->next.port = d->port;
-}
+#define IP_SET_EMIT_CREATE
+#include "ip_set_hash_gen.h"
 
 static int
 hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
 		   const struct xt_action_param *par,
-		   enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+		   enum ipset_adt adt, struct ip_set_adt_opt *opt)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_netport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netport6_elem data = {
+	struct hash_netport6_elem e = {
 		.cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1,
 	};
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
 
 	if (adt == IPSET_TEST)
-		data.cidr = HOST_MASK - 1;
+		e.cidr = HOST_MASK - 1;
 
 	if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
-				 &data.port, &data.proto))
+				 &e.port, &e.proto))
 		return -EINVAL;
 
-	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6);
-	ip6_netmask(&data.ip, data.cidr + 1);
+	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
+	ip6_netmask(&e.ip, e.cidr + 1);
 
-	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
+	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 }
 
 static int
 hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		   enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
-	const struct ip_set_hash *h = set->data;
+	const struct hash_netport *h = set->data;
 	ipset_adtfn adtfn = set->variant->adt[adt];
-	struct hash_netport6_elem data = { .cidr = HOST_MASK  - 1 };
+	struct hash_netport6_elem e = { .cidr = HOST_MASK  - 1 };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(h);
 	u32 port, port_to;
-	u32 timeout = h->timeout;
 	bool with_ports = false;
 	u8 cidr;
 	int ret;
@@ -518,7 +408,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip);
+	ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) ||
+	      ip_set_get_extensions(set, tb, &ext);
 	if (ret)
 		return ret;
 
@@ -526,32 +417,26 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 		if (!cidr || cidr > HOST_MASK)
 			return -IPSET_ERR_INVALID_CIDR;
-		data.cidr = cidr - 1;
+		e.cidr = cidr - 1;
 	}
-	ip6_netmask(&data.ip, data.cidr + 1);
+	ip6_netmask(&e.ip, e.cidr + 1);
 
 	if (tb[IPSET_ATTR_PORT])
-		data.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
+		e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
 	else
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_PROTO]) {
-		data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
-		with_ports = ip_set_proto_with_ports(data.proto);
+		e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]);
+		with_ports = ip_set_proto_with_ports(e.proto);
 
-		if (data.proto == 0)
+		if (e.proto == 0)
 			return -IPSET_ERR_INVALID_PROTO;
 	} else
 		return -IPSET_ERR_MISSING_PROTO;
 
-	if (!(with_ports || data.proto == IPPROTO_ICMPV6))
-		data.port = 0;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout(h->timeout))
-			return -IPSET_ERR_TIMEOUT;
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-	}
+	if (!(with_ports || e.proto == IPPROTO_ICMPV6))
+		e.port = 0;
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
@@ -560,12 +445,12 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	}
 
 	if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) {
-		ret = adtfn(set, &data, timeout, flags);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 		return ip_set_enomatch(ret, flags, adt) ? 1 :
 		       ip_set_eexist(ret, flags) ? 0 : ret;
 	}
 
-	port = ntohs(data.port);
+	port = ntohs(e.port);
 	port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]);
 	if (port > port_to)
 		swap(port, port_to);
@@ -573,8 +458,8 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (retried)
 		port = ntohs(h->next.port);
 	for (; port <= port_to; port++) {
-		data.port = htons(port);
-		ret = adtfn(set, &data, timeout, flags);
+		e.port = htons(port);
+		ret = adtfn(set, &e, &ext, &ext, flags);
 
 		if (ret && !ip_set_eexist(ret, flags))
 			return ret;
@@ -584,80 +469,6 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 	return ret;
 }
 
-/* Create hash:ip type of sets */
-
-static int
-hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
-{
-	struct ip_set_hash *h;
-	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
-	u8 hbits;
-	size_t hsize;
-
-	if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
-		return -IPSET_ERR_INVALID_FAMILY;
-
-	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
-		return -IPSET_ERR_PROTOCOL;
-
-	if (tb[IPSET_ATTR_HASHSIZE]) {
-		hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]);
-		if (hashsize < IPSET_MIMINAL_HASHSIZE)
-			hashsize = IPSET_MIMINAL_HASHSIZE;
-	}
-
-	if (tb[IPSET_ATTR_MAXELEM])
-		maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
-
-	h = kzalloc(sizeof(*h)
-		    + sizeof(struct ip_set_hash_nets)
-		      * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	h->maxelem = maxelem;
-	get_random_bytes(&h->initval, sizeof(h->initval));
-	h->timeout = IPSET_NO_TIMEOUT;
-
-	hbits = htable_bits(hashsize);
-	hsize = htable_size(hbits);
-	if (hsize == 0) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table = ip_set_alloc(hsize);
-	if (!h->table) {
-		kfree(h);
-		return -ENOMEM;
-	}
-	h->table->htable_bits = hbits;
-
-	set->data = h;
-
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
-
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_netport4_tvariant : &hash_netport6_tvariant;
-
-		if (set->family == NFPROTO_IPV4)
-			hash_netport4_gc_init(set);
-		else
-			hash_netport6_gc_init(set);
-	} else {
-		set->variant = set->family == NFPROTO_IPV4
-			? &hash_netport4_variant : &hash_netport6_variant;
-	}
-
-	pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
-		 set->name, jhash_size(h->table->htable_bits),
-		 h->table->htable_bits, h->maxelem, set->data, h->table);
-
-	return 0;
-}
-
 static struct ip_set_type hash_netport_type __read_mostly = {
 	.name		= "hash:net,port",
 	.protocol	= IPSET_PROTOCOL,
@@ -674,6 +485,7 @@ static struct ip_set_type hash_netport_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
-- 
1.7.10.4

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

* [PATCH 08/18] netfilter: ipset: list:set type using the extension interface
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (6 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 07/18] netfilter: ipset: Hash types using the unified code base Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 09/18] netfilter: ipset: Introduce the counter extension in the core Pablo Neira Ayuso
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_list_set.c |  547 ++++++++++++++++++---------------
 1 file changed, 301 insertions(+), 246 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 09c744a..919eefe 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2008-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -13,7 +13,6 @@
 #include <linux/errno.h>
 
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_list.h>
 
 #define REVISION_MIN	0
@@ -24,19 +23,28 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_list:set");
 
-/* Member elements without and with timeout */
+/* Member elements  */
 struct set_elem {
 	ip_set_id_t id;
 };
 
-struct set_telem {
-	ip_set_id_t id;
+struct sett_elem {
+	struct {
+		ip_set_id_t id;
+	} __attribute__ ((aligned));
 	unsigned long timeout;
 };
 
+struct set_adt_elem {
+	ip_set_id_t id;
+	ip_set_id_t refid;
+	int before;
+};
+
 /* Type structure */
 struct list_set {
 	size_t dsize;		/* element size */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
 	u32 size;		/* size of set list array */
 	u32 timeout;		/* timeout value */
 	struct timer_list gc;	/* garbage collection */
@@ -49,179 +57,296 @@ list_set_elem(const struct list_set *map, u32 id)
 	return (struct set_elem *)((void *)map->members + id * map->dsize);
 }
 
-static inline struct set_telem *
-list_set_telem(const struct list_set *map, u32 id)
-{
-	return (struct set_telem *)((void *)map->members + id * map->dsize);
-}
+#define ext_timeout(e, m)	\
+(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
 
-static inline bool
-list_set_timeout(const struct list_set *map, u32 id)
+static int
+list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
+	       const struct xt_action_param *par,
+	       struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
-	const struct set_telem *elem = list_set_telem(map, id);
+	struct list_set *map = set->data;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	return ip_set_timeout_test(elem->timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return 0;
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		ret = ip_set_test(e->id, skb, par, opt);
+		if (ret > 0)
+			return ret;
+	}
+	return 0;
 }
 
-static inline bool
-list_set_expired(const struct list_set *map, u32 id)
+static int
+list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
+	      const struct xt_action_param *par,
+	      struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
-	const struct set_telem *elem = list_set_telem(map, id);
+	struct list_set *map = set->data;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	return ip_set_timeout_expired(elem->timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return 0;
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		ret = ip_set_add(e->id, skb, par, opt);
+		if (ret == 0)
+			return ret;
+	}
+	return 0;
 }
 
-/* Set list without and with timeout */
-
 static int
-list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
+list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
 	      const struct xt_action_param *par,
-	      enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	      struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
 	struct list_set *map = set->data;
-	struct set_elem *elem;
+	struct set_elem *e;
 	u32 i;
 	int ret;
 
 	for (i = 0; i < map->size; i++) {
-		elem = list_set_elem(map, i);
-		if (elem->id == IPSET_INVALID_ID)
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
 			return 0;
-		if (with_timeout(map->timeout) && list_set_expired(map, i))
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
 			continue;
-		switch (adt) {
-		case IPSET_TEST:
-			ret = ip_set_test(elem->id, skb, par, opt);
-			if (ret > 0)
-				return ret;
-			break;
-		case IPSET_ADD:
-			ret = ip_set_add(elem->id, skb, par, opt);
-			if (ret == 0)
-				return ret;
-			break;
-		case IPSET_DEL:
-			ret = ip_set_del(elem->id, skb, par, opt);
-			if (ret == 0)
-				return ret;
-			break;
-		default:
-			break;
-		}
+		ret = ip_set_del(e->id, skb, par, opt);
+		if (ret == 0)
+			return ret;
+	}
+	return 0;
+}
+
+static int
+list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
+	      const struct xt_action_param *par,
+	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+	struct list_set *map = set->data;
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+
+	switch (adt) {
+	case IPSET_TEST:
+		return list_set_ktest(set, skb, par, opt, &ext);
+	case IPSET_ADD:
+		return list_set_kadd(set, skb, par, opt, &ext);
+	case IPSET_DEL:
+		return list_set_kdel(set, skb, par, opt, &ext);
+	default:
+		break;
 	}
 	return -EINVAL;
 }
 
 static bool
-id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
+id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
 {
-	const struct set_elem *elem;
+	const struct list_set *map = set->data;
+	const struct set_elem *e;
+
+	if (i >= map->size)
+		return 0;
 
-	if (i < map->size) {
-		elem = list_set_elem(map, i);
-		return elem->id == id;
+	e = list_set_elem(map, i);
+	return !!(e->id == id &&
+		 !(SET_WITH_TIMEOUT(set) &&
+		   ip_set_timeout_expired(ext_timeout(e, map))));
+}
+
+static int
+list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
+	     const struct ip_set_ext *ext)
+{
+	struct list_set *map = set->data;
+	struct set_elem *e = list_set_elem(map, i);
+
+	if (e->id != IPSET_INVALID_ID) {
+		if (i == map->size - 1)
+			/* Last element replaced: e.g. add new,before,last */
+			ip_set_put_byindex(e->id);
+		else {
+			struct set_elem *x = list_set_elem(map, map->size - 1);
+
+			/* Last element pushed off */
+			if (x->id != IPSET_INVALID_ID)
+				ip_set_put_byindex(x->id);
+			memmove(list_set_elem(map, i + 1), e,
+				map->dsize * (map->size - (i + 1)));
+		}
 	}
 
+	e->id = d->id;
+	if (SET_WITH_TIMEOUT(set))
+		ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
 	return 0;
 }
 
-static bool
-id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
+static int
+list_set_del(struct ip_set *set, u32 i)
 {
-	const struct set_elem *elem;
+	struct list_set *map = set->data;
+	struct set_elem *e = list_set_elem(map, i);
 
-	if (i < map->size) {
-		elem = list_set_elem(map, i);
-		return !!(elem->id == id &&
-			  !(with_timeout(map->timeout) &&
-			    list_set_expired(map, i)));
-	}
+	ip_set_put_byindex(e->id);
 
+	if (i < map->size - 1)
+		memmove(e, list_set_elem(map, i + 1),
+			map->dsize * (map->size - (i + 1)));
+
+	/* Last element */
+	e = list_set_elem(map, map->size - 1);
+	e->id = IPSET_INVALID_ID;
 	return 0;
 }
 
 static void
-list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
+set_cleanup_entries(struct ip_set *set)
 {
+	struct list_set *map = set->data;
 	struct set_elem *e;
+	u32 i;
 
-	for (; i < map->size; i++) {
+	for (i = 0; i < map->size; i++) {
 		e = list_set_elem(map, i);
-		swap(e->id, id);
-		if (e->id == IPSET_INVALID_ID)
-			break;
+		if (e->id != IPSET_INVALID_ID &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			list_set_del(set, i);
 	}
 }
 
-static void
-list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
-	       unsigned long timeout)
+static int
+list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	       struct ip_set_ext *mext, u32 flags)
 {
-	struct set_telem *e;
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	for (; i < map->size; i++) {
-		e = list_set_telem(map, i);
-		swap(e->id, id);
-		swap(e->timeout, timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
 		if (e->id == IPSET_INVALID_ID)
-			break;
+			return 0;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
+
+		if (d->before == 0)
+			return 1;
+		else if (d->before > 0)
+			ret = id_eq(set, i + 1, d->refid);
+		else
+			ret = i > 0 && id_eq(set, i - 1, d->refid);
+		return ret;
 	}
+	return 0;
 }
 
+
 static int
-list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
-	     unsigned long timeout)
+list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	      struct ip_set_ext *mext, u32 flags)
 {
-	const struct set_elem *e = list_set_elem(map, i);
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
+	bool flag_exist = flags & IPSET_FLAG_EXIST;
+	u32 i, ret = 0;
 
-	if (e->id != IPSET_INVALID_ID) {
-		const struct set_elem *x = list_set_elem(map, map->size - 1);
+	/* Check already added element */
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			goto insert;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
 
-		/* Last element replaced or pushed off */
-		if (x->id != IPSET_INVALID_ID)
-			ip_set_put_byindex(x->id);
+		if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) ||
+		    (d->before < 0 &&
+		     (i == 0 || !id_eq(set, i - 1, d->refid))))
+			/* Before/after doesn't match */
+			return -IPSET_ERR_REF_EXIST;
+		if (!flag_exist)
+			/* Can't re-add */
+			return -IPSET_ERR_EXIST;
+		/* Update extensions */
+		if (SET_WITH_TIMEOUT(set))
+			ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+		/* Set is already added to the list */
+		ip_set_put_byindex(d->id);
+		return 0;
+	}
+insert:
+	ret = -IPSET_ERR_LIST_FULL;
+	for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
+				: list_set_add(set, i, d, ext);
+		else if (e->id != d->refid)
+			continue;
+		else if (d->before > 0)
+			ret = list_set_add(set, i, d, ext);
+		else if (i + 1 < map->size)
+			ret = list_set_add(set, i + 1, d, ext);
 	}
-	if (with_timeout(map->timeout))
-		list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
-	else
-		list_elem_add(map, i, id);
 
-	return 0;
+	return ret;
 }
 
 static int
-list_set_del(struct list_set *map, u32 i)
+list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	      struct ip_set_ext *mext, u32 flags)
 {
-	struct set_elem *a = list_set_elem(map, i), *b;
-
-	ip_set_put_byindex(a->id);
-
-	for (; i < map->size - 1; i++) {
-		b = list_set_elem(map, i + 1);
-		a->id = b->id;
-		if (with_timeout(map->timeout))
-			((struct set_telem *)a)->timeout =
-				((struct set_telem *)b)->timeout;
-		a = b;
-		if (a->id == IPSET_INVALID_ID)
-			break;
-	}
-	/* Last element */
-	a->id = IPSET_INVALID_ID;
-	return 0;
-}
-
-static void
-cleanup_entries(struct list_set *map)
-{
-	struct set_telem *e;
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
 	u32 i;
 
 	for (i = 0; i < map->size; i++) {
-		e = list_set_telem(map, i);
-		if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
-			list_set_del(map, i);
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return d->before != 0 ? -IPSET_ERR_REF_EXIST
+					      : -IPSET_ERR_EXIST;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
+
+		if (d->before == 0)
+			return list_set_del(set, i);
+		else if (d->before > 0) {
+			if (!id_eq(set, i + 1, d->refid))
+				return -IPSET_ERR_REF_EXIST;
+			return list_set_del(set, i);
+		} else if (i == 0 || !id_eq(set, i - 1, d->refid))
+			return -IPSET_ERR_REF_EXIST;
+		else
+			return list_set_del(set, i);
 	}
+	return -IPSET_ERR_EXIST;
 }
 
 static int
@@ -229,14 +354,10 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
 	struct list_set *map = set->data;
-	bool with_timeout = with_timeout(map->timeout);
-	bool flag_exist = flags & IPSET_FLAG_EXIST;
-	int before = 0;
-	u32 timeout = map->timeout;
-	ip_set_id_t id, refid = IPSET_INVALID_ID;
-	const struct set_elem *elem;
+	ipset_adtfn adtfn = set->variant->adt[adt];
+	struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
 	struct ip_set *s;
-	u32 i;
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_NAME] ||
@@ -247,8 +368,11 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
-	if (id == IPSET_INVALID_ID)
+	ret = ip_set_get_extensions(set, tb, &ext);
+	if (ret)
+		return ret;
+	e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+	if (e.id == IPSET_INVALID_ID)
 		return -IPSET_ERR_NAME;
 	/* "Loop detection" */
 	if (s->type->features & IPSET_TYPE_NAME) {
@@ -258,115 +382,34 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-		before = f & IPSET_FLAG_BEFORE;
+		e.before = f & IPSET_FLAG_BEFORE;
 	}
 
-	if (before && !tb[IPSET_ATTR_NAMEREF]) {
+	if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
 		ret = -IPSET_ERR_BEFORE;
 		goto finish;
 	}
 
 	if (tb[IPSET_ATTR_NAMEREF]) {
-		refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
-					  &s);
-		if (refid == IPSET_INVALID_ID) {
+		e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+					    &s);
+		if (e.refid == IPSET_INVALID_ID) {
 			ret = -IPSET_ERR_NAMEREF;
 			goto finish;
 		}
-		if (!before)
-			before = -1;
-	}
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout) {
-			ret = -IPSET_ERR_TIMEOUT;
-			goto finish;
-		}
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+		if (!e.before)
+			e.before = -1;
 	}
-	if (with_timeout && adt != IPSET_TEST)
-		cleanup_entries(map);
+	if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
+		set_cleanup_entries(set);
 
-	switch (adt) {
-	case IPSET_TEST:
-		for (i = 0; i < map->size && !ret; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID ||
-			    (before != 0 && i + 1 >= map->size))
-				break;
-			else if (with_timeout && list_set_expired(map, i))
-				continue;
-			else if (before > 0 && elem->id == id)
-				ret = id_eq_timeout(map, i + 1, refid);
-			else if (before < 0 && elem->id == refid)
-				ret = id_eq_timeout(map, i + 1, id);
-			else if (before == 0 && elem->id == id)
-				ret = 1;
-		}
-		break;
-	case IPSET_ADD:
-		for (i = 0; i < map->size; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id != id)
-				continue;
-			if (!(with_timeout && flag_exist)) {
-				ret = -IPSET_ERR_EXIST;
-				goto finish;
-			} else {
-				struct set_telem *e = list_set_telem(map, i);
-
-				if ((before > 1 &&
-				     !id_eq(map, i + 1, refid)) ||
-				    (before < 0 &&
-				     (i == 0 || !id_eq(map, i - 1, refid)))) {
-					ret = -IPSET_ERR_EXIST;
-					goto finish;
-				}
-				e->timeout = ip_set_timeout_set(timeout);
-				ip_set_put_byindex(id);
-				ret = 0;
-				goto finish;
-			}
-		}
-		ret = -IPSET_ERR_LIST_FULL;
-		for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID)
-				ret = before != 0 ? -IPSET_ERR_REF_EXIST
-					: list_set_add(map, i, id, timeout);
-			else if (elem->id != refid)
-				continue;
-			else if (before > 0)
-				ret = list_set_add(map, i, id, timeout);
-			else if (i + 1 < map->size)
-				ret = list_set_add(map, i + 1, id, timeout);
-		}
-		break;
-	case IPSET_DEL:
-		ret = -IPSET_ERR_EXIST;
-		for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID) {
-				ret = before != 0 ? -IPSET_ERR_REF_EXIST
-						  : -IPSET_ERR_EXIST;
-				break;
-			} else if (elem->id == id &&
-				   (before == 0 ||
-				    (before > 0 && id_eq(map, i + 1, refid))))
-				ret = list_set_del(map, i);
-			else if (elem->id == refid &&
-				 before < 0 && id_eq(map, i + 1, id))
-				ret = list_set_del(map, i + 1);
-		}
-		break;
-	default:
-		break;
-	}
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 finish:
-	if (refid != IPSET_INVALID_ID)
-		ip_set_put_byindex(refid);
+	if (e.refid != IPSET_INVALID_ID)
+		ip_set_put_byindex(e.refid);
 	if (adt != IPSET_ADD || ret)
-		ip_set_put_byindex(id);
+		ip_set_put_byindex(e.id);
 
 	return ip_set_eexist(ret, flags) ? 0 : ret;
 }
@@ -375,14 +418,14 @@ static void
 list_set_flush(struct ip_set *set)
 {
 	struct list_set *map = set->data;
-	struct set_elem *elem;
+	struct set_elem *e;
 	u32 i;
 
 	for (i = 0; i < map->size; i++) {
-		elem = list_set_elem(map, i);
-		if (elem->id != IPSET_INVALID_ID) {
-			ip_set_put_byindex(elem->id);
-			elem->id = IPSET_INVALID_ID;
+		e = list_set_elem(map, i);
+		if (e->id != IPSET_INVALID_ID) {
+			ip_set_put_byindex(e->id);
+			e->id = IPSET_INVALID_ID;
 		}
 	}
 }
@@ -392,7 +435,7 @@ list_set_destroy(struct ip_set *set)
 {
 	struct list_set *map = set->data;
 
-	if (with_timeout(map->timeout))
+	if (SET_WITH_TIMEOUT(set))
 		del_timer_sync(&map->gc);
 	list_set_flush(set);
 	kfree(map);
@@ -410,7 +453,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
 	if (!nested)
 		goto nla_put_failure;
 	if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
-	    (with_timeout(map->timeout) &&
+	    (SET_WITH_TIMEOUT(set) &&
 	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
 	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
 	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
@@ -440,7 +483,8 @@ list_set_list(const struct ip_set *set,
 		e = list_set_elem(map, i);
 		if (e->id == IPSET_INVALID_ID)
 			goto finish;
-		if (with_timeout(map->timeout) && list_set_expired(map, i))
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
 			continue;
 		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 		if (!nested) {
@@ -453,13 +497,11 @@ list_set_list(const struct ip_set *set,
 		if (nla_put_string(skb, IPSET_ATTR_NAME,
 				   ip_set_name_byindex(e->id)))
 			goto nla_put_failure;
-		if (with_timeout(map->timeout)) {
-			const struct set_telem *te =
-				(const struct set_telem *) e;
-			__be32 to = htonl(ip_set_timeout_get(te->timeout));
-			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
-				goto nla_put_failure;
-		}
+		if (SET_WITH_TIMEOUT(set) &&
+		    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+				  htonl(ip_set_timeout_get(
+						ext_timeout(e, map)))))
+			goto nla_put_failure;
 		ipset_nest_end(skb, nested);
 	}
 finish:
@@ -485,12 +527,18 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
 	const struct list_set *y = b->data;
 
 	return x->size == y->size &&
-	       x->timeout == y->timeout;
+	       x->timeout == y->timeout &&
+	       a->extensions == b->extensions;
 }
 
-static const struct ip_set_type_variant list_set = {
+static const struct ip_set_type_variant set_variant = {
 	.kadt	= list_set_kadt,
 	.uadt	= list_set_uadt,
+	.adt	= {
+		[IPSET_ADD] = list_set_uadd,
+		[IPSET_DEL] = list_set_udel,
+		[IPSET_TEST] = list_set_utest,
+	},
 	.destroy = list_set_destroy,
 	.flush	= list_set_flush,
 	.head	= list_set_head,
@@ -505,7 +553,7 @@ list_set_gc(unsigned long ul_set)
 	struct list_set *map = set->data;
 
 	write_lock_bh(&set->lock);
-	cleanup_entries(map);
+	set_cleanup_entries(set);
 	write_unlock_bh(&set->lock);
 
 	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
@@ -513,20 +561,20 @@ list_set_gc(unsigned long ul_set)
 }
 
 static void
-list_set_gc_init(struct ip_set *set)
+list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
 {
 	struct list_set *map = set->data;
 
 	init_timer(&map->gc);
 	map->gc.data = (unsigned long) set;
-	map->gc.function = list_set_gc;
+	map->gc.function = gc;
 	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
 	add_timer(&map->gc);
 }
 
 /* Create list:set type of sets */
 
-static bool
+static struct list_set *
 init_list_set(struct ip_set *set, u32 size, size_t dsize,
 	      unsigned long timeout)
 {
@@ -536,7 +584,7 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
 
 	map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
 	if (!map)
-		return false;
+		return NULL;
 
 	map->size = size;
 	map->dsize = dsize;
@@ -548,13 +596,15 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
 		e->id = IPSET_INVALID_ID;
 	}
 
-	return true;
+	return map;
 }
 
 static int
 list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
+	struct list_set *map;
 	u32 size = IP_SET_LIST_DEFAULT_SIZE;
+	unsigned long timeout = 0;
 
 	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -565,18 +615,23 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	if (size < IP_SET_LIST_MIN_SIZE)
 		size = IP_SET_LIST_MIN_SIZE;
 
+	if (tb[IPSET_ATTR_TIMEOUT])
+		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+	set->variant = &set_variant;
 	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!init_list_set(set, size, sizeof(struct set_telem),
-				   ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
+		map = init_list_set(set, size,
+				    sizeof(struct sett_elem), timeout);
+		if (!map)
 			return -ENOMEM;
-
-		list_set_gc_init(set);
+		set->extensions |= IPSET_EXT_TIMEOUT;
+		map->offset[IPSET_OFFSET_TIMEOUT] =
+			offsetof(struct sett_elem, timeout);
+		list_set_gc_init(set, list_set_gc);
 	} else {
-		if (!init_list_set(set, size, sizeof(struct set_elem),
-				   IPSET_NO_TIMEOUT))
+		map = init_list_set(set, size, sizeof(struct set_elem), 0);
+		if (!map)
 			return -ENOMEM;
 	}
-	set->variant = &list_set;
 	return 0;
 }
 
-- 
1.7.10.4

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

* [PATCH 09/18] netfilter: ipset: Introduce the counter extension in the core
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (7 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 08/18] netfilter: ipset: list:set type using the extension interface Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 10/18] netfilter: ipset: The bitmap types with counter support Pablo Neira Ayuso
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set.h      |   75 +++++++++++++++++++++++++--
 include/uapi/linux/netfilter/ipset/ip_set.h |    5 ++
 net/netfilter/ipset/ip_set_core.c           |   10 ++++
 3 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index bf0220c..0f978eb 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -52,18 +52,24 @@ enum ip_set_extension {
 	IPSET_EXT_NONE = 0,
 	IPSET_EXT_BIT_TIMEOUT = 1,
 	IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT),
+	IPSET_EXT_BIT_COUNTER = 2,
+	IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER),
 };
 
 /* Extension offsets */
 enum ip_set_offset {
 	IPSET_OFFSET_TIMEOUT = 0,
+	IPSET_OFFSET_COUNTER,
 	IPSET_OFFSET_MAX,
 };
 
 #define SET_WITH_TIMEOUT(s)	((s)->extensions & IPSET_EXT_TIMEOUT)
+#define SET_WITH_COUNTER(s)	((s)->extensions & IPSET_EXT_COUNTER)
 
 struct ip_set_ext {
 	unsigned long timeout;
+	u64 packets;
+	u64 bytes;
 };
 
 struct ip_set;
@@ -177,6 +183,65 @@ struct ip_set {
 	void *data;
 };
 
+struct ip_set_counter {
+	atomic64_t bytes;
+	atomic64_t packets;
+};
+
+static inline void
+ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter)
+{
+	atomic64_add((long long)bytes, &(counter)->bytes);
+}
+
+static inline void
+ip_set_add_packets(u64 packets, struct ip_set_counter *counter)
+{
+	atomic64_add((long long)packets, &(counter)->packets);
+}
+
+static inline u64
+ip_set_get_bytes(const struct ip_set_counter *counter)
+{
+	return (u64)atomic64_read(&(counter)->bytes);
+}
+
+static inline u64
+ip_set_get_packets(const struct ip_set_counter *counter)
+{
+	return (u64)atomic64_read(&(counter)->packets);
+}
+
+static inline void
+ip_set_update_counter(struct ip_set_counter *counter,
+		      const struct ip_set_ext *ext,
+		      struct ip_set_ext *mext, u32 flags)
+{
+	if (ext->packets != ULLONG_MAX) {
+		ip_set_add_bytes(ext->bytes, counter);
+		ip_set_add_packets(ext->packets, counter);
+	}
+}
+
+static inline bool
+ip_set_put_counter(struct sk_buff *skb, struct ip_set_counter *counter)
+{
+	return nla_put_net64(skb, IPSET_ATTR_BYTES,
+			     cpu_to_be64(ip_set_get_bytes(counter))) ||
+	       nla_put_net64(skb, IPSET_ATTR_PACKETS,
+			     cpu_to_be64(ip_set_get_packets(counter)));
+}
+
+static inline void
+ip_set_init_counter(struct ip_set_counter *counter,
+		    const struct ip_set_ext *ext)
+{
+	if (ext->bytes != ULLONG_MAX)
+		atomic64_set(&(counter)->bytes, (long long)(ext->bytes));
+	if (ext->packets != ULLONG_MAX)
+		atomic64_set(&(counter)->packets, (long long)(ext->packets));
+}
+
 /* register and unregister set references */
 extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set);
 extern void ip_set_put_byindex(ip_set_id_t index);
@@ -318,10 +383,12 @@ bitmap_bytes(u32 a, u32 b)
 
 #include <linux/netfilter/ipset/ip_set_timeout.h>
 
-#define IP_SET_INIT_KEXT(skb, opt, map)		\
-	{ .timeout = ip_set_adt_opt_timeout(opt, map) }
+#define IP_SET_INIT_KEXT(skb, opt, map)			\
+	{ .bytes = (skb)->len, .packets = 1,		\
+	  .timeout = ip_set_adt_opt_timeout(opt, map) }
 
-#define IP_SET_INIT_UEXT(map)			\
-	{ .timeout = (map)->timeout }
+#define IP_SET_INIT_UEXT(map)				\
+	{ .bytes = ULLONG_MAX, .packets = ULLONG_MAX,	\
+	  .timeout = (map)->timeout }
 
 #endif /*_IP_SET_H */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
index fbee428..ed45267 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -108,6 +108,8 @@ enum {
 	IPSET_ATTR_CIDR2,
 	IPSET_ATTR_IP2_TO,
 	IPSET_ATTR_IFACE,
+	IPSET_ATTR_BYTES,
+	IPSET_ATTR_PACKETS,
 	__IPSET_ATTR_ADT_MAX,
 };
 #define IPSET_ATTR_ADT_MAX	(__IPSET_ATTR_ADT_MAX - 1)
@@ -137,6 +139,7 @@ enum ipset_errno {
 	IPSET_ERR_REFERENCED,
 	IPSET_ERR_IPADDR_IPV4,
 	IPSET_ERR_IPADDR_IPV6,
+	IPSET_ERR_COUNTER,
 
 	/* Type specific error codes */
 	IPSET_ERR_TYPE_SPECIFIC = 4352,
@@ -161,6 +164,8 @@ enum ipset_cadt_flags {
 	IPSET_FLAG_PHYSDEV	= (1 << IPSET_FLAG_BIT_PHYSDEV),
 	IPSET_FLAG_BIT_NOMATCH	= 2,
 	IPSET_FLAG_NOMATCH	= (1 << IPSET_FLAG_BIT_NOMATCH),
+	IPSET_FLAG_BIT_WITH_COUNTERS = 3,
+	IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
 	IPSET_FLAG_CADT_MAX	= 15,	/* Upper half */
 };
 
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 4486285d..f6d878a 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -324,6 +324,16 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[],
 			return -IPSET_ERR_TIMEOUT;
 		ext->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	}
+	if (tb[IPSET_ATTR_BYTES] || tb[IPSET_ATTR_PACKETS]) {
+		if (!(set->extensions & IPSET_EXT_COUNTER))
+			return -IPSET_ERR_COUNTER;
+		if (tb[IPSET_ATTR_BYTES])
+			ext->bytes = be64_to_cpu(nla_get_be64(
+						 tb[IPSET_ATTR_BYTES]));
+		if (tb[IPSET_ATTR_PACKETS])
+			ext->packets = be64_to_cpu(nla_get_be64(
+						   tb[IPSET_ATTR_PACKETS]));
+	}
 	return 0;
 }
 EXPORT_SYMBOL_GPL(ip_set_get_extensions);
-- 
1.7.10.4

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

* [PATCH 10/18] netfilter: ipset: The bitmap types with counter support
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (8 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 09/18] netfilter: ipset: Introduce the counter extension in the core Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 11/18] netfilter: ipset: The hash " Pablo Neira Ayuso
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_bitmap_gen.h   |   14 +++++-
 net/netfilter/ipset/ip_set_bitmap_ip.c    |   62 +++++++++++++++++++++++---
 net/netfilter/ipset/ip_set_bitmap_ipmac.c |   68 ++++++++++++++++++++++++++---
 net/netfilter/ipset/ip_set_bitmap_port.c  |   57 ++++++++++++++++++++++--
 4 files changed, 186 insertions(+), 15 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
index b993159..2524337 100644
--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -37,6 +37,8 @@
 
 #define ext_timeout(e, m)	\
 	(unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
+#define ext_counter(e, m)	\
+	(struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER])
 #define get_ext(map, id)	((map)->extensions + (map)->dsize * (id))
 
 static void
@@ -91,7 +93,10 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 				map->memsize +
 				map->dsize * map->elements)) ||
 	    (SET_WITH_TIMEOUT(set) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
+	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
+	    (SET_WITH_COUNTER(set) &&
+	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
+			   htonl(IPSET_FLAG_WITH_COUNTERS))))
 		goto nla_put_failure;
 	ipset_nest_end(skb, nested);
 
@@ -114,6 +119,8 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 	if (SET_WITH_TIMEOUT(set) &&
 	    ip_set_timeout_expired(ext_timeout(x, map)))
 		return 0;
+	if (SET_WITH_COUNTER(set))
+		ip_set_update_counter(ext_counter(x, map), ext, mext, flags);
 	return 1;
 }
 
@@ -141,6 +148,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 		ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
 #endif
 
+	if (SET_WITH_COUNTER(set))
+		ip_set_init_counter(ext_counter(x, map), ext);
 	return 0;
 }
 
@@ -205,6 +214,9 @@ mtype_list(const struct ip_set *set,
 				goto nla_put_failure;
 #endif
 		}
+		if (SET_WITH_COUNTER(set) &&
+		    ip_set_put_counter(skb, ext_counter(x, map)))
+			goto nla_put_failure;
 		ipset_nest_end(skb, nested);
 	}
 	ipset_nest_end(skb, adt);
diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c
index f2ab011..f1a8128 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ip.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ip.c
@@ -26,7 +26,7 @@
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	0
+#define REVISION_MAX	1	/* Counter support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -137,7 +137,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[],
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -213,6 +215,19 @@ struct bitmap_ipt_elem {
 	unsigned long timeout;
 };
 
+/* Plain variant with counter */
+
+struct bitmap_ipc_elem {
+	struct ip_set_counter counter;
+};
+
+/* Timeout variant with counter */
+
+struct bitmap_ipct_elem {
+	unsigned long timeout;
+	struct ip_set_counter counter;
+};
+
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -249,13 +264,14 @@ static int
 bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
 	struct bitmap_ip *map;
-	u32 first_ip, last_ip, hosts;
+	u32 first_ip, last_ip, hosts, cadt_flags = 0;
 	u64 elements;
 	u8 netmask = 32;
 	int ret;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 		return -IPSET_ERR_PROTOCOL;
 
 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
@@ -320,7 +336,40 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 
 	map->memsize = bitmap_bytes(0, elements - 1);
 	set->variant = &bitmap_ip;
-	if (tb[IPSET_ATTR_TIMEOUT]) {
+	if (tb[IPSET_ATTR_CADT_FLAGS])
+		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+	if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
+		set->extensions |= IPSET_EXT_COUNTER;
+		if (tb[IPSET_ATTR_TIMEOUT]) {
+			map->dsize = sizeof(struct bitmap_ipct_elem);
+			map->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct bitmap_ipct_elem, timeout);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_ipct_elem, counter);
+
+			if (!init_map_ip(set, map, first_ip, last_ip,
+					 elements, hosts, netmask)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+
+			map->timeout = ip_set_timeout_uget(
+				tb[IPSET_ATTR_TIMEOUT]);
+			set->extensions |= IPSET_EXT_TIMEOUT;
+
+			bitmap_ip_gc_init(set, bitmap_ip_gc);
+		} else {
+			map->dsize = sizeof(struct bitmap_ipc_elem);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_ipc_elem, counter);
+
+			if (!init_map_ip(set, map, first_ip, last_ip,
+					 elements, hosts, netmask)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+		}
+	} else if (tb[IPSET_ATTR_TIMEOUT]) {
 		map->dsize = sizeof(struct bitmap_ipt_elem);
 		map->offset[IPSET_OFFSET_TIMEOUT] =
 			offsetof(struct bitmap_ipt_elem, timeout);
@@ -361,6 +410,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_NETMASK]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
@@ -368,6 +418,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = {
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
index 960810d..3b30e0b 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
@@ -26,7 +26,7 @@
 #include <linux/netfilter/ipset/ip_set_bitmap.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	0
+#define REVISION_MAX	1	/* Counter support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -250,7 +250,9 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[],
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -299,6 +301,27 @@ struct bitmap_ipmact_elem {
 	unsigned long timeout;
 };
 
+/* Plain variant with counter */
+
+struct bitmap_ipmacc_elem {
+	struct {
+		unsigned char ether[ETH_ALEN];
+		unsigned char filled;
+	} __attribute__ ((aligned));
+	struct ip_set_counter counter;
+};
+
+/* Timeout variant with counter */
+
+struct bitmap_ipmacct_elem {
+	struct {
+		unsigned char ether[ETH_ALEN];
+		unsigned char filled;
+	} __attribute__ ((aligned));
+	unsigned long timeout;
+	struct ip_set_counter counter;
+};
+
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip,mac type of sets */
@@ -332,13 +355,14 @@ static int
 bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
 		    u32 flags)
 {
-	u32 first_ip, last_ip;
+	u32 first_ip, last_ip, cadt_flags = 0;
 	u64 elements;
 	struct bitmap_ipmac *map;
 	int ret;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 		return -IPSET_ERR_PROTOCOL;
 
 	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip);
@@ -375,7 +399,38 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[],
 
 	map->memsize = bitmap_bytes(0, elements - 1);
 	set->variant = &bitmap_ipmac;
-	if (tb[IPSET_ATTR_TIMEOUT]) {
+	if (tb[IPSET_ATTR_CADT_FLAGS])
+		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+	if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
+		set->extensions |= IPSET_EXT_COUNTER;
+		if (tb[IPSET_ATTR_TIMEOUT]) {
+			map->dsize = sizeof(struct bitmap_ipmacct_elem);
+			map->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct bitmap_ipmacct_elem, timeout);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_ipmacct_elem, counter);
+
+			if (!init_map_ipmac(set, map, first_ip, last_ip,
+					    elements)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+			map->timeout = ip_set_timeout_uget(
+				tb[IPSET_ATTR_TIMEOUT]);
+			set->extensions |= IPSET_EXT_TIMEOUT;
+			bitmap_ipmac_gc_init(set, bitmap_ipmac_gc);
+		} else {
+			map->dsize = sizeof(struct bitmap_ipmacc_elem);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_ipmacc_elem, counter);
+
+			if (!init_map_ipmac(set, map, first_ip, last_ip,
+					    elements)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+		}
+	} else if (tb[IPSET_ATTR_TIMEOUT]) {
 		map->dsize = sizeof(struct bitmap_ipmact_elem);
 		map->offset[IPSET_OFFSET_TIMEOUT] =
 			offsetof(struct bitmap_ipmact_elem, timeout);
@@ -413,6 +468,7 @@ static struct ip_set_type bitmap_ipmac_type = {
 		[IPSET_ATTR_IP_TO]	= { .type = NLA_NESTED },
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
@@ -420,6 +476,8 @@ static struct ip_set_type bitmap_ipmac_type = {
 					    .len  = ETH_ALEN },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c
index 27e2c57..8207d1f 100644
--- a/net/netfilter/ipset/ip_set_bitmap_port.c
+++ b/net/netfilter/ipset/ip_set_bitmap_port.c
@@ -21,7 +21,7 @@
 #include <linux/netfilter/ipset/ip_set_getport.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	0
+#define REVISION_MAX	1	/* Counter support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -138,7 +138,9 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -199,10 +201,24 @@ struct bitmap_port_elem {
 };
 
 /* Timeout variant */
+
 struct bitmap_portt_elem {
 	unsigned long timeout;
 };
 
+/* Plain variant with counter */
+
+struct bitmap_portc_elem {
+	struct ip_set_counter counter;
+};
+
+/* Timeout variant with counter */
+
+struct bitmap_portct_elem {
+	unsigned long timeout;
+	struct ip_set_counter counter;
+};
+
 #include "ip_set_bitmap_gen.h"
 
 /* Create bitmap:ip type of sets */
@@ -236,10 +252,12 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
 	struct bitmap_port *map;
 	u16 first_port, last_port;
+	u32 cadt_flags = 0;
 
 	if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 		return -IPSET_ERR_PROTOCOL;
 
 	first_port = ip_set_get_h16(tb[IPSET_ATTR_PORT]);
@@ -258,7 +276,35 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	map->elements = last_port - first_port + 1;
 	map->memsize = map->elements * sizeof(unsigned long);
 	set->variant = &bitmap_port;
-	if (tb[IPSET_ATTR_TIMEOUT]) {
+	if (tb[IPSET_ATTR_CADT_FLAGS])
+		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+	if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
+		set->extensions |= IPSET_EXT_COUNTER;
+		if (tb[IPSET_ATTR_TIMEOUT]) {
+			map->dsize = sizeof(struct bitmap_portct_elem);
+			map->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct bitmap_portct_elem, timeout);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_portct_elem, counter);
+			if (!init_map_port(set, map, first_port, last_port)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+
+			map->timeout =
+				ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+			set->extensions |= IPSET_EXT_TIMEOUT;
+			bitmap_port_gc_init(set, bitmap_port_gc);
+		} else {
+			map->dsize = sizeof(struct bitmap_portc_elem);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct bitmap_portc_elem, counter);
+			if (!init_map_port(set, map, first_port, last_port)) {
+				kfree(map);
+				return -ENOMEM;
+			}
+		}
+	} else if (tb[IPSET_ATTR_TIMEOUT]) {
 		map->dsize = sizeof(struct bitmap_portt_elem);
 		map->offset[IPSET_OFFSET_TIMEOUT] =
 			offsetof(struct bitmap_portt_elem, timeout);
@@ -294,12 +340,15 @@ static struct ip_set_type bitmap_port_type = {
 		[IPSET_ATTR_PORT]	= { .type = NLA_U16 },
 		[IPSET_ATTR_PORT_TO]	= { .type = NLA_U16 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_PORT]	= { .type = NLA_U16 },
 		[IPSET_ATTR_PORT_TO]	= { .type = NLA_U16 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
-- 
1.7.10.4

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

* [PATCH 11/18] netfilter: ipset: The hash types with counter support
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (9 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 10/18] netfilter: ipset: The bitmap types with counter support Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 12/18] netfilter: ipset: The list:set type " Pablo Neira Ayuso
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_hash_gen.h       |   65 ++++++++++++++++++++++++++-
 net/netfilter/ipset/ip_set_hash_ip.c        |   33 +++++++++++++-
 net/netfilter/ipset/ip_set_hash_ipport.c    |   46 ++++++++++++++++++-
 net/netfilter/ipset/ip_set_hash_ipportip.c  |   50 ++++++++++++++++++++-
 net/netfilter/ipset/ip_set_hash_ipportnet.c |   53 +++++++++++++++++++++-
 net/netfilter/ipset/ip_set_hash_net.c       |   47 +++++++++++++++++--
 net/netfilter/ipset/ip_set_hash_netiface.c  |   55 +++++++++++++++++++++--
 net/netfilter/ipset/ip_set_hash_netport.c   |   51 +++++++++++++++++++--
 8 files changed, 381 insertions(+), 19 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index 2ba7d4e..57beb17 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -177,6 +177,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
 
 #define ext_timeout(e, h)	\
 (unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT])
+#define ext_counter(e, h)	\
+(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER])
 
 #endif /* _IP_SET_HASH_GEN_H */
 
@@ -660,6 +662,8 @@ reuse_slot:
 #endif
 	if (SET_WITH_TIMEOUT(set))
 		ip_set_timeout_set(ext_timeout(data, h), ext->timeout);
+	if (SET_WITH_COUNTER(set))
+		ip_set_init_counter(ext_counter(data, h), ext);
 
 out:
 	rcu_read_unlock_bh();
@@ -721,6 +725,10 @@ static inline int
 mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext,
 		 struct ip_set_ext *mext, struct ip_set *set, u32 flags)
 {
+	if (SET_WITH_COUNTER(set))
+		ip_set_update_counter(ext_counter(data,
+						  (struct htype *)(set->data)),
+				      ext, mext, flags);
 	return mtype_do_data_match(data);
 }
 
@@ -826,7 +834,10 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 	if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
 	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
 	    ((set->extensions & IPSET_EXT_TIMEOUT) &&
-	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))))
+	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) ||
+	    ((set->extensions & IPSET_EXT_COUNTER) &&
+	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
+			   htonl(IPSET_FLAG_WITH_COUNTERS))))
 		goto nla_put_failure;
 	ipset_nest_end(skb, nested);
 
@@ -880,6 +891,9 @@ mtype_list(const struct ip_set *set,
 					  htonl(ip_set_timeout_get(
 						ext_timeout(e, h)))))
 				goto nla_put_failure;
+			if (SET_WITH_COUNTER(set) &&
+			    ip_set_put_counter(skb, ext_counter(e, h)))
+				goto nla_put_failure;
 			ipset_nest_end(skb, nested);
 		}
 	}
@@ -931,6 +945,7 @@ static int
 TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
 	u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
+	u32 cadt_flags = 0;
 	u8 hbits;
 #ifdef IP_SET_HASH_WITH_NETMASK
 	u8 netmask;
@@ -1007,7 +1022,53 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	else
 		set->variant = &TOKEN(HTYPE, 6_variant);
 
-	if (tb[IPSET_ATTR_TIMEOUT]) {
+	if (tb[IPSET_ATTR_CADT_FLAGS])
+		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
+	if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
+		set->extensions |= IPSET_EXT_COUNTER;
+		if (tb[IPSET_ATTR_TIMEOUT]) {
+			h->timeout =
+				ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+			set->extensions |= IPSET_EXT_TIMEOUT;
+			if (set->family == NFPROTO_IPV4) {
+				h->dsize =
+					sizeof(struct TOKEN(HTYPE, 4ct_elem));
+				h->offset[IPSET_OFFSET_TIMEOUT] =
+					offsetof(struct TOKEN(HTYPE, 4ct_elem),
+						 timeout);
+				h->offset[IPSET_OFFSET_COUNTER] =
+					offsetof(struct TOKEN(HTYPE, 4ct_elem),
+						 counter);
+				TOKEN(HTYPE, 4_gc_init)(set,
+					TOKEN(HTYPE, 4_gc));
+			} else {
+				h->dsize =
+					sizeof(struct TOKEN(HTYPE, 6ct_elem));
+				h->offset[IPSET_OFFSET_TIMEOUT] =
+					offsetof(struct TOKEN(HTYPE, 6ct_elem),
+						 timeout);
+				h->offset[IPSET_OFFSET_COUNTER] =
+					offsetof(struct TOKEN(HTYPE, 6ct_elem),
+						 counter);
+				TOKEN(HTYPE, 6_gc_init)(set,
+					TOKEN(HTYPE, 6_gc));
+			}
+		} else {
+			if (set->family == NFPROTO_IPV4) {
+				h->dsize =
+					sizeof(struct TOKEN(HTYPE, 4c_elem));
+				h->offset[IPSET_OFFSET_COUNTER] =
+					offsetof(struct TOKEN(HTYPE, 4c_elem),
+						 counter);
+			} else {
+				h->dsize =
+					sizeof(struct TOKEN(HTYPE, 6c_elem));
+				h->offset[IPSET_OFFSET_COUNTER] =
+					offsetof(struct TOKEN(HTYPE, 6c_elem),
+						 counter);
+			}
+		}
+	} else if (tb[IPSET_ATTR_TIMEOUT]) {
 		h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 		set->extensions |= IPSET_EXT_TIMEOUT;
 		if (set->family == NFPROTO_IPV4) {
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index 2fcfb21..c74e6e1 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -24,7 +24,7 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	0
+#define REVISION_MAX	1	/* Counters support */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -48,6 +48,17 @@ struct hash_ip4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ip4c_elem {
+	__be32 ip;
+	struct ip_set_counter counter;
+};
+
+struct hash_ip4ct_elem {
+	__be32 ip;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -112,7 +123,9 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -177,6 +190,17 @@ struct hash_ip6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ip6c_elem {
+	union nf_inet_addr ip;
+	struct ip_set_counter counter;
+};
+
+struct hash_ip6ct_elem {
+	union nf_inet_addr ip;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -251,6 +275,8 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
 		     tb[IPSET_ATTR_IP_TO] ||
 		     tb[IPSET_ATTR_CIDR]))
 		return -IPSET_ERR_PROTOCOL;
@@ -288,6 +314,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_NETMASK]	= { .type = NLA_U8  },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
@@ -295,6 +322,8 @@ static struct ip_set_type hash_ip_type __read_mostly = {
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index d89cf41..7a2d2bd 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -25,7 +25,8 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	1 /* SCTP and UDPLITE support added */
+/*			1    SCTP and UDPLITE support added */
+#define REVISION_MAX	2 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -53,6 +54,23 @@ struct hash_ipport4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipport4c_elem {
+	__be32 ip;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipport4ct_elem {
+	__be32 ip;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -126,7 +144,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -219,6 +239,23 @@ struct hash_ipport6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipport6c_elem {
+	union nf_inet_addr ip;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipport6ct_elem {
+	union nf_inet_addr ip;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -298,6 +335,8 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
 		     tb[IPSET_ATTR_IP_TO] ||
 		     tb[IPSET_ATTR_CIDR]))
 		return -IPSET_ERR_PROTOCOL;
@@ -367,6 +406,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
@@ -377,6 +417,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index 4b58e5c..34e8a1a 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -25,7 +25,8 @@
 #include <linux/netfilter/ipset/ip_set_hash.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	1 /* SCTP and UDPLITE support added */
+/*			1    SCTP and UDPLITE support added */
+#define REVISION_MAX	2 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -55,6 +56,25 @@ struct hash_ipportip4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipportip4c_elem {
+	__be32 ip;
+	__be32 ip2;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipportip4ct_elem {
+	__be32 ip;
+	__be32 ip2;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 static inline bool
 hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
 			  const struct hash_ipportip4_elem *ip2,
@@ -129,7 +149,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] ||
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -228,6 +250,25 @@ struct hash_ipportip6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipportip6c_elem {
+	union nf_inet_addr ip;
+	union nf_inet_addr ip2;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipportip6ct_elem {
+	union nf_inet_addr ip;
+	union nf_inet_addr ip2;
+	__be16 port;
+	u8 proto;
+	u8 padding;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -308,6 +349,8 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
 		     tb[IPSET_ATTR_IP_TO] ||
 		     tb[IPSET_ATTR_CIDR]))
 		return -IPSET_ERR_PROTOCOL;
@@ -380,6 +423,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
 		[IPSET_ATTR_PROBES]	= { .type = NLA_U8 },
 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_IP]		= { .type = NLA_NESTED },
@@ -391,6 +435,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = {
 		[IPSET_ATTR_PROTO]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index bfde4ea..c6a5253 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -27,7 +27,8 @@
 #define REVISION_MIN	0
 /*			1    SCTP and UDPLITE support added */
 /*			2    Range as input support for IPv4 added */
-#define REVISION_MAX	3 /* nomatch flag support added */
+/*			3    nomatch flag support added */
+#define REVISION_MAX	4 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -67,6 +68,27 @@ struct hash_ipportnet4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipportnet4c_elem {
+	__be32 ip;
+	__be32 ip2;
+	__be16 port;
+	u8 cidr:7;
+	u8 nomatch:1;
+	u8 proto;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipportnet4ct_elem {
+	__be32 ip;
+	__be32 ip2;
+	__be16 port;
+	u8 cidr:7;
+	u8 nomatch:1;
+	u8 proto;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -184,7 +206,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -325,6 +349,27 @@ struct hash_ipportnet6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_ipportnet6c_elem {
+	union nf_inet_addr ip;
+	union nf_inet_addr ip2;
+	__be16 port;
+	u8 cidr:7;
+	u8 nomatch:1;
+	u8 proto;
+	struct ip_set_counter counter;
+};
+
+struct hash_ipportnet6ct_elem {
+	union nf_inet_addr ip;
+	union nf_inet_addr ip2;
+	__be16 port;
+	u8 cidr:7;
+	u8 nomatch:1;
+	u8 proto;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -445,6 +490,8 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) ||
 		     tb[IPSET_ATTR_IP_TO] ||
 		     tb[IPSET_ATTR_CIDR]))
 		return -IPSET_ERR_PROTOCOL;
@@ -551,6 +598,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = {
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index cfbcdd4..da740ce 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -24,7 +24,8 @@
 
 #define REVISION_MIN	0
 /*			1    Range as input support for IPv4 added */
-#define REVISION_MAX	2 /* nomatch flag support added */
+/*			2    nomatch flag support added */
+#define REVISION_MAX	3 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -53,6 +54,23 @@ struct hash_net4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_net4c_elem {
+	__be32 ip;
+	u16 padding0;
+	u8 nomatch;
+	u8 cidr;
+	struct ip_set_counter counter;
+};
+
+struct hash_net4ct_elem {
+	__be32 ip;
+	u16 padding0;
+	u8 nomatch;
+	u8 cidr;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -153,7 +171,9 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -225,6 +245,23 @@ struct hash_net6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_net6c_elem {
+	union nf_inet_addr ip;
+	u16 padding0;
+	u8 nomatch;
+	u8 cidr;
+	struct ip_set_counter counter;
+};
+
+struct hash_net6ct_elem {
+	union nf_inet_addr ip;
+	u16 padding0;
+	u8 nomatch;
+	u8 cidr;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -328,7 +365,9 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 	if (unlikely(tb[IPSET_ATTR_IP_TO]))
 		return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -384,6 +423,8 @@ static struct ip_set_type hash_net_type __read_mostly = {
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index 555ebb7..84ae6f6 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -25,7 +25,8 @@
 
 #define REVISION_MIN	0
 /*			1    nomatch flag support added */
-#define REVISION_MAX	2 /* /0 support added */
+/*			2    /0 support added */
+#define REVISION_MAX	3 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -163,6 +164,27 @@ struct hash_netiface4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_netiface4c_elem {
+	__be32 ip;
+	u8 physdev;
+	u8 cidr;
+	u8 nomatch;
+	u8 elem;
+	const char *iface;
+	struct ip_set_counter counter;
+};
+
+struct hash_netiface4ct_elem {
+	__be32 ip;
+	u8 physdev;
+	u8 cidr;
+	u8 nomatch;
+	u8 elem;
+	const char *iface;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -305,7 +327,9 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !tb[IPSET_ATTR_IFACE] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -404,6 +428,27 @@ struct hash_netiface6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_netiface6c_elem {
+	union nf_inet_addr ip;
+	u8 physdev;
+	u8 cidr;
+	u8 nomatch;
+	u8 elem;
+	const char *iface;
+	struct ip_set_counter counter;
+};
+
+struct hash_netiface6ct_elem {
+	union nf_inet_addr ip;
+	u8 physdev;
+	u8 cidr;
+	u8 nomatch;
+	u8 elem;
+	const char *iface;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -546,7 +591,9 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (unlikely(!tb[IPSET_ATTR_IP] ||
 		     !tb[IPSET_ATTR_IFACE] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 	if (unlikely(tb[IPSET_ATTR_IP_TO]))
 		return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -619,6 +666,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
 		[IPSET_ATTR_CIDR]	= { .type = NLA_U8 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 43b1f1e..9a08698 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -26,7 +26,8 @@
 #define REVISION_MIN	0
 /*			1    SCTP and UDPLITE support added */
 /*			2    Range as input support for IPv4 added */
-#define REVISION_MAX	3 /* nomatch flag support added */
+/*			3    nomatch flag support added */
+#define REVISION_MAX	4 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -64,6 +65,25 @@ struct hash_netport4t_elem {
 	unsigned long timeout;
 };
 
+struct hash_netport4c_elem {
+	__be32 ip;
+	__be16 port;
+	u8 proto;
+	u8 cidr:7;
+	u8 nomatch:1;
+	struct ip_set_counter counter;
+};
+
+struct hash_netport4ct_elem {
+	__be32 ip;
+	__be16 port;
+	u8 proto;
+	u8 cidr:7;
+	u8 nomatch:1;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -176,7 +196,9 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -284,6 +306,25 @@ struct hash_netport6t_elem {
 	unsigned long timeout;
 };
 
+struct hash_netport6c_elem {
+	union nf_inet_addr ip;
+	__be16 port;
+	u8 proto;
+	u8 cidr:7;
+	u8 nomatch:1;
+	struct ip_set_counter counter;
+};
+
+struct hash_netport6ct_elem {
+	union nf_inet_addr ip;
+	__be16 port;
+	u8 proto;
+	u8 cidr:7;
+	u8 nomatch:1;
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 /* Common functions */
 
 static inline bool
@@ -400,7 +441,9 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[],
 		     !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 	if (unlikely(tb[IPSET_ATTR_IP_TO]))
 		return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
@@ -497,6 +540,8 @@ static struct ip_set_type hash_netport_type __read_mostly = {
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
-- 
1.7.10.4

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

* [PATCH 12/18] netfilter: ipset: The list:set type with counter support
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (10 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 11/18] netfilter: ipset: The hash " Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 13/18] netfilter: ipset: set match: add support to match the counters Pablo Neira Ayuso
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipset/ip_set_list_set.c |   73 ++++++++++++++++++++++++++++++---
 1 file changed, 67 insertions(+), 6 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 919eefe..c09022e 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -16,7 +16,7 @@
 #include <linux/netfilter/ipset/ip_set_list.h>
 
 #define REVISION_MIN	0
-#define REVISION_MAX	0
+#define REVISION_MAX	1 /* Counters support added */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
@@ -35,6 +35,21 @@ struct sett_elem {
 	unsigned long timeout;
 };
 
+struct setc_elem {
+	struct {
+		ip_set_id_t id;
+	} __attribute__ ((aligned));
+	struct ip_set_counter counter;
+};
+
+struct setct_elem {
+	struct {
+		ip_set_id_t id;
+	} __attribute__ ((aligned));
+	struct ip_set_counter counter;
+	unsigned long timeout;
+};
+
 struct set_adt_elem {
 	ip_set_id_t id;
 	ip_set_id_t refid;
@@ -59,6 +74,8 @@ list_set_elem(const struct list_set *map, u32 id)
 
 #define ext_timeout(e, m)	\
 (unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
+#define ext_counter(e, m)	\
+(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER])
 
 static int
 list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
@@ -78,8 +95,13 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
 		    ip_set_timeout_expired(ext_timeout(e, map)))
 			continue;
 		ret = ip_set_test(e->id, skb, par, opt);
-		if (ret > 0)
+		if (ret > 0) {
+			if (SET_WITH_COUNTER(set))
+				ip_set_update_counter(ext_counter(e, map),
+						      ext, &opt->ext,
+						      opt->cmdflags);
 			return ret;
+		}
 	}
 	return 0;
 }
@@ -193,6 +215,8 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
 	e->id = d->id;
 	if (SET_WITH_TIMEOUT(set))
 		ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+	if (SET_WITH_COUNTER(set))
+		ip_set_init_counter(ext_counter(e, map), ext);
 	return 0;
 }
 
@@ -293,6 +317,8 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 		/* Update extensions */
 		if (SET_WITH_TIMEOUT(set))
 			ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+		if (SET_WITH_COUNTER(set))
+			ip_set_init_counter(ext_counter(e, map), ext);
 		/* Set is already added to the list */
 		ip_set_put_byindex(d->id);
 		return 0;
@@ -362,7 +388,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (unlikely(!tb[IPSET_ATTR_NAME] ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_LINENO])
@@ -455,6 +483,9 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
 	if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
 	    (SET_WITH_TIMEOUT(set) &&
 	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
+	    (SET_WITH_COUNTER(set) &&
+	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
+			   htonl(IPSET_FLAG_WITH_COUNTERS))) ||
 	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
 	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
 			  htonl(sizeof(*map) + map->size * map->dsize)))
@@ -502,6 +533,9 @@ list_set_list(const struct ip_set *set,
 				  htonl(ip_set_timeout_get(
 						ext_timeout(e, map)))))
 			goto nla_put_failure;
+		if (SET_WITH_COUNTER(set) &&
+		    ip_set_put_counter(skb, ext_counter(e, map)))
+			goto nla_put_failure;
 		ipset_nest_end(skb, nested);
 	}
 finish:
@@ -603,11 +637,12 @@ static int
 list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
 	struct list_set *map;
-	u32 size = IP_SET_LIST_DEFAULT_SIZE;
+	u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0;
 	unsigned long timeout = 0;
 
 	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
-		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
+		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 		return -IPSET_ERR_PROTOCOL;
 
 	if (tb[IPSET_ATTR_SIZE])
@@ -615,10 +650,33 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	if (size < IP_SET_LIST_MIN_SIZE)
 		size = IP_SET_LIST_MIN_SIZE;
 
+	if (tb[IPSET_ATTR_CADT_FLAGS])
+		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 	if (tb[IPSET_ATTR_TIMEOUT])
 		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 	set->variant = &set_variant;
-	if (tb[IPSET_ATTR_TIMEOUT]) {
+	if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) {
+		set->extensions |= IPSET_EXT_COUNTER;
+		if (tb[IPSET_ATTR_TIMEOUT]) {
+			map = init_list_set(set, size,
+					sizeof(struct setct_elem), timeout);
+			if (!map)
+				return -ENOMEM;
+			set->extensions |= IPSET_EXT_TIMEOUT;
+			map->offset[IPSET_OFFSET_TIMEOUT] =
+				offsetof(struct setct_elem, timeout);
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct setct_elem, counter);
+			list_set_gc_init(set, list_set_gc);
+		} else {
+			map = init_list_set(set, size,
+					    sizeof(struct setc_elem), 0);
+			if (!map)
+				return -ENOMEM;
+			map->offset[IPSET_OFFSET_COUNTER] =
+				offsetof(struct setc_elem, counter);
+		}
+	} else if (tb[IPSET_ATTR_TIMEOUT]) {
 		map = init_list_set(set, size,
 				    sizeof(struct sett_elem), timeout);
 		if (!map)
@@ -647,6 +705,7 @@ static struct ip_set_type list_set_type __read_mostly = {
 	.create_policy	= {
 		[IPSET_ATTR_SIZE]	= { .type = NLA_U32 },
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
+		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
 	},
 	.adt_policy	= {
 		[IPSET_ATTR_NAME]	= { .type = NLA_STRING,
@@ -656,6 +715,8 @@ static struct ip_set_type list_set_type __read_mostly = {
 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
+		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
+		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
 	},
 	.me		= THIS_MODULE,
 };
-- 
1.7.10.4


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

* [PATCH 13/18] netfilter: ipset: set match: add support to match the counters
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (11 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 12/18] netfilter: ipset: The list:set type " Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 14/18] netfilter: nf_queue: move device refcount bump to extra function Pablo Neira Ayuso
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

The new revision of the set match supports to match the counters
and to suppress updating the counters at matching too.

At the set:list types, the updating of the subcounters can be
suppressed as well.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/ipset/ip_set.h      |    9 +++-
 include/uapi/linux/netfilter/ipset/ip_set.h |   31 ++++++++++--
 include/uapi/linux/netfilter/xt_set.h       |    9 ++++
 net/netfilter/ipset/ip_set_core.c           |    2 +-
 net/netfilter/ipset/ip_set_list_set.c       |    8 ++-
 net/netfilter/xt_set.c                      |   70 +++++++++++++++++++++++++++
 6 files changed, 120 insertions(+), 9 deletions(-)

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 0f978eb..d80e275 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -76,7 +76,7 @@ struct ip_set;
 
 typedef int (*ipset_adtfn)(struct ip_set *set, void *value,
 			   const struct ip_set_ext *ext,
-			   struct ip_set_ext *mext, u32 flags);
+			   struct ip_set_ext *mext, u32 cmdflags);
 
 /* Kernel API function options */
 struct ip_set_adt_opt {
@@ -217,10 +217,15 @@ ip_set_update_counter(struct ip_set_counter *counter,
 		      const struct ip_set_ext *ext,
 		      struct ip_set_ext *mext, u32 flags)
 {
-	if (ext->packets != ULLONG_MAX) {
+	if (ext->packets != ULLONG_MAX &&
+	    !(flags & IPSET_FLAG_SKIP_COUNTER_UPDATE)) {
 		ip_set_add_bytes(ext->bytes, counter);
 		ip_set_add_packets(ext->packets, counter);
 	}
+	if (flags & IPSET_FLAG_MATCH_COUNTERS) {
+		mext->packets = ip_set_get_packets(counter);
+		mext->bytes = ip_set_get_bytes(counter);
+	}
 }
 
 static inline bool
diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
index ed45267..8024cdf 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -145,7 +145,7 @@ enum ipset_errno {
 	IPSET_ERR_TYPE_SPECIFIC = 4352,
 };
 
-/* Flags at command level */
+/* Flags at command level or match/target flags, lower half of cmdattrs*/
 enum ipset_cmd_flags {
 	IPSET_FLAG_BIT_EXIST	= 0,
 	IPSET_FLAG_EXIST	= (1 << IPSET_FLAG_BIT_EXIST),
@@ -153,10 +153,20 @@ enum ipset_cmd_flags {
 	IPSET_FLAG_LIST_SETNAME	= (1 << IPSET_FLAG_BIT_LIST_SETNAME),
 	IPSET_FLAG_BIT_LIST_HEADER = 2,
 	IPSET_FLAG_LIST_HEADER	= (1 << IPSET_FLAG_BIT_LIST_HEADER),
-	IPSET_FLAG_CMD_MAX = 15,	/* Lower half */
+	IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE = 3,
+	IPSET_FLAG_SKIP_COUNTER_UPDATE =
+		(1 << IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE),
+	IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE = 4,
+	IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE =
+		(1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE),
+	IPSET_FLAG_BIT_MATCH_COUNTERS = 5,
+	IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
+	IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
+	IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
+	IPSET_FLAG_CMD_MAX = 15,
 };
 
-/* Flags at CADT attribute level */
+/* Flags at CADT attribute level, upper half of cmdattrs */
 enum ipset_cadt_flags {
 	IPSET_FLAG_BIT_BEFORE	= 0,
 	IPSET_FLAG_BEFORE	= (1 << IPSET_FLAG_BIT_BEFORE),
@@ -166,7 +176,7 @@ enum ipset_cadt_flags {
 	IPSET_FLAG_NOMATCH	= (1 << IPSET_FLAG_BIT_NOMATCH),
 	IPSET_FLAG_BIT_WITH_COUNTERS = 3,
 	IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
-	IPSET_FLAG_CADT_MAX	= 15,	/* Upper half */
+	IPSET_FLAG_CADT_MAX	= 15,
 };
 
 /* Commands with settype-specific attributes */
@@ -195,6 +205,7 @@ enum ip_set_dim {
 	 * If changed, new revision of iptables match/target is required.
 	 */
 	IPSET_DIM_MAX = 6,
+	/* Backward compatibility: set match revision 2 */
 	IPSET_BIT_RETURN_NOMATCH = 7,
 };
 
@@ -207,6 +218,18 @@ enum ip_set_kopt {
 	IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
 };
 
+enum {
+	IPSET_COUNTER_NONE = 0,
+	IPSET_COUNTER_EQ,
+	IPSET_COUNTER_NE,
+	IPSET_COUNTER_LT,
+	IPSET_COUNTER_GT,
+};
+
+struct ip_set_counter_match {
+	__u8 op;
+	__u64 value;
+};
 
 /* Interface to iptables/ip6tables */
 
diff --git a/include/uapi/linux/netfilter/xt_set.h b/include/uapi/linux/netfilter/xt_set.h
index e3a9978..964d3d4 100644
--- a/include/uapi/linux/netfilter/xt_set.h
+++ b/include/uapi/linux/netfilter/xt_set.h
@@ -62,4 +62,13 @@ struct xt_set_info_target_v2 {
 	__u32 timeout;
 };
 
+/* Revision 3 match */
+
+struct xt_set_info_match_v3 {
+	struct xt_set_info match_set;
+	struct ip_set_counter_match packets;
+	struct ip_set_counter_match bytes;
+	__u32 flags;
+};
+
 #endif /*_XT_SET_H*/
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index f6d878a..f771390 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -413,7 +413,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
 		ret = 1;
 	} else {
 		/* --return-nomatch: invert matched element */
-		if ((opt->flags & IPSET_RETURN_NOMATCH) &&
+		if ((opt->cmdflags & IPSET_FLAG_RETURN_NOMATCH) &&
 		    (set->type->features & IPSET_TYPE_NOMATCH) &&
 		    (ret > 0 || ret == -ENOTEMPTY))
 			ret = -ret;
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index c09022e..979b8c9 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -84,9 +84,13 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
 {
 	struct list_set *map = set->data;
 	struct set_elem *e;
-	u32 i;
+	u32 i, cmdflags = opt->cmdflags;
 	int ret;
 
+	/* Don't lookup sub-counters at all */
+	opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
+	if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
+		opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
 	for (i = 0; i < map->size; i++) {
 		e = list_set_elem(map, i);
 		if (e->id == IPSET_INVALID_ID)
@@ -99,7 +103,7 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
 			if (SET_WITH_COUNTER(set))
 				ip_set_update_counter(ext_counter(e, map),
 						      ext, &opt->ext,
-						      opt->cmdflags);
+						      cmdflags);
 			return ret;
 		}
 	}
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c
index 636c519..31790e7 100644
--- a/net/netfilter/xt_set.c
+++ b/net/netfilter/xt_set.c
@@ -189,6 +189,9 @@ set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
 	ADT_OPT(opt, par->family, info->match_set.dim,
 		info->match_set.flags, 0, UINT_MAX);
 
+	if (opt.flags & IPSET_RETURN_NOMATCH)
+		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
+
 	return match_set(info->match_set.index, skb, par, &opt,
 			 info->match_set.flags & IPSET_INV_MATCH);
 }
@@ -317,6 +320,52 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 #define set_target_v2_checkentry	set_target_v1_checkentry
 #define set_target_v2_destroy		set_target_v1_destroy
 
+/* Revision 3 match */
+
+static bool
+match_counter(u64 counter, const struct ip_set_counter_match *info)
+{
+	switch (info->op) {
+	case IPSET_COUNTER_NONE:
+		return true;
+	case IPSET_COUNTER_EQ:
+		return counter == info->value;
+	case IPSET_COUNTER_NE:
+		return counter != info->value;
+	case IPSET_COUNTER_LT:
+		return counter < info->value;
+	case IPSET_COUNTER_GT:
+		return counter > info->value;
+	}
+	return false;
+}
+
+static bool
+set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
+{
+	const struct xt_set_info_match_v3 *info = par->matchinfo;
+	ADT_OPT(opt, par->family, info->match_set.dim,
+		info->match_set.flags, info->flags, UINT_MAX);
+	int ret;
+
+	if (info->packets.op != IPSET_COUNTER_NONE ||
+	    info->bytes.op != IPSET_COUNTER_NONE)
+		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
+
+	ret = match_set(info->match_set.index, skb, par, &opt,
+			info->match_set.flags & IPSET_INV_MATCH);
+
+	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
+		return ret;
+
+	if (!match_counter(opt.ext.packets, &info->packets))
+		return 0;
+	return match_counter(opt.ext.bytes, &info->bytes);
+}
+
+#define set_match_v3_checkentry	set_match_v1_checkentry
+#define set_match_v3_destroy	set_match_v1_destroy
+
 static struct xt_match set_matches[] __read_mostly = {
 	{
 		.name		= "set",
@@ -369,6 +418,27 @@ static struct xt_match set_matches[] __read_mostly = {
 		.destroy	= set_match_v1_destroy,
 		.me		= THIS_MODULE
 	},
+	/* counters support: update, match */
+	{
+		.name		= "set",
+		.family		= NFPROTO_IPV4,
+		.revision	= 3,
+		.match		= set_match_v3,
+		.matchsize	= sizeof(struct xt_set_info_match_v3),
+		.checkentry	= set_match_v3_checkentry,
+		.destroy	= set_match_v3_destroy,
+		.me		= THIS_MODULE
+	},
+	{
+		.name		= "set",
+		.family		= NFPROTO_IPV6,
+		.revision	= 3,
+		.match		= set_match_v3,
+		.matchsize	= sizeof(struct xt_set_info_match_v3),
+		.checkentry	= set_match_v3_checkentry,
+		.destroy	= set_match_v3_destroy,
+		.me		= THIS_MODULE
+	},
 };
 
 static struct xt_target set_targets[] __read_mostly = {
-- 
1.7.10.4


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

* [PATCH 14/18] netfilter: nf_queue: move device refcount bump to extra function
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (12 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 13/18] netfilter: ipset: set match: add support to match the counters Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 15/18] netfilter: move skb_gso_segment into nfnetlink_queue module Pablo Neira Ayuso
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Florian Westphal <fw@strlen.de>

required by future patch that will need to duplicate the
nf_queue_entry, bumping refcounts of the copy.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_queue.c |   49 ++++++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 21 deletions(-)

diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 5ccf01e..1d91e77 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -66,6 +66,33 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
 	module_put(entry->elem->owner);
 }
 
+/* Bump dev refs so they don't vanish while packet is out */
+static bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
+{
+	if (!try_module_get(entry->elem->owner))
+		return false;
+
+	if (entry->indev)
+		dev_hold(entry->indev);
+	if (entry->outdev)
+		dev_hold(entry->outdev);
+#ifdef CONFIG_BRIDGE_NETFILTER
+	if (entry->skb->nf_bridge) {
+		struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
+		struct net_device *physdev;
+
+		physdev = nf_bridge->physindev;
+		if (physdev)
+			dev_hold(physdev);
+		physdev = nf_bridge->physoutdev;
+		if (physdev)
+			dev_hold(physdev);
+	}
+#endif
+
+	return true;
+}
+
 /*
  * Any packet that leaves via this function must come back
  * through nf_reinject().
@@ -80,10 +107,6 @@ static int __nf_queue(struct sk_buff *skb,
 {
 	int status = -ENOENT;
 	struct nf_queue_entry *entry = NULL;
-#ifdef CONFIG_BRIDGE_NETFILTER
-	struct net_device *physindev;
-	struct net_device *physoutdev;
-#endif
 	const struct nf_afinfo *afinfo;
 	const struct nf_queue_handler *qh;
 
@@ -116,26 +139,10 @@ static int __nf_queue(struct sk_buff *skb,
 		.okfn	= okfn,
 	};
 
-	/* If it's going away, ignore hook. */
-	if (!try_module_get(entry->elem->owner)) {
+	if (!nf_queue_entry_get_refs(entry)) {
 		status = -ECANCELED;
 		goto err_unlock;
 	}
-	/* Bump dev refs so they don't vanish while packet is out */
-	if (indev)
-		dev_hold(indev);
-	if (outdev)
-		dev_hold(outdev);
-#ifdef CONFIG_BRIDGE_NETFILTER
-	if (skb->nf_bridge) {
-		physindev = skb->nf_bridge->physindev;
-		if (physindev)
-			dev_hold(physindev);
-		physoutdev = skb->nf_bridge->physoutdev;
-		if (physoutdev)
-			dev_hold(physoutdev);
-	}
-#endif
 	skb_dst_force(skb);
 	afinfo->saveroute(skb, entry);
 	status = qh->outfn(entry, queuenum);
-- 
1.7.10.4

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

* [PATCH 15/18] netfilter: move skb_gso_segment into nfnetlink_queue module
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (13 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 14/18] netfilter: nf_queue: move device refcount bump to extra function Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 16/18] netfilter: nfnetlink_queue: add skb info attribute Pablo Neira Ayuso
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Florian Westphal <fw@strlen.de>

skb_gso_segment is expensive, so it would be nice if we could
avoid it in the future. However, userspace needs to be prepared
to receive larger-than-mtu-packets (which will also have incorrect
l3/l4 checksums), so we cannot simply remove it.

The plan is to add a per-queue feature flag that userspace can
set when binding the queue.

The problem is that in nf_queue, we only have a queue number,
not the queue context/configuration settings.

This patch should have no impact other than the skb_gso_segment
call now being in a function that has access to the queue config
data.

A new size attribute in nf_queue_entry is needed so
nfnetlink_queue can duplicate the entry of the gso skb
when segmenting the skb while also copying the route key.

The follow up patch adds switch to disable skb_gso_segment when
queue config says so.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/net/netfilter/nf_queue.h     |    6 ++
 net/netfilter/nf_queue.c             |   96 ++-------------------
 net/netfilter/nfnetlink_queue_core.c |  154 ++++++++++++++++++++++++++++++----
 3 files changed, 152 insertions(+), 104 deletions(-)

diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index fb1c0be..aaba4bb 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -9,10 +9,13 @@ struct nf_queue_entry {
 
 	struct nf_hook_ops	*elem;
 	u_int8_t		pf;
+	u16			size; /* sizeof(entry) + saved route keys */
 	unsigned int		hook;
 	struct net_device	*indev;
 	struct net_device	*outdev;
 	int			(*okfn)(struct sk_buff *);
+
+	/* extra space to store route keys */
 };
 
 #define nf_queue_entry_reroute(x) ((void *)x + sizeof(struct nf_queue_entry))
@@ -27,4 +30,7 @@ void nf_register_queue_handler(const struct nf_queue_handler *qh);
 void nf_unregister_queue_handler(void);
 extern void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
 
+bool nf_queue_entry_get_refs(struct nf_queue_entry *entry);
+void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
+
 #endif /* _NF_QUEUE_H */
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 1d91e77..5d24b1f 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -45,7 +45,7 @@ void nf_unregister_queue_handler(void)
 }
 EXPORT_SYMBOL(nf_unregister_queue_handler);
 
-static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
+void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
 {
 	/* Release those devices we held, or Alexey will kill me. */
 	if (entry->indev)
@@ -65,9 +65,10 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
 	/* Drop reference to owner of hook which queued us. */
 	module_put(entry->elem->owner);
 }
+EXPORT_SYMBOL_GPL(nf_queue_entry_release_refs);
 
 /* Bump dev refs so they don't vanish while packet is out */
-static bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
+bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
 {
 	if (!try_module_get(entry->elem->owner))
 		return false;
@@ -92,12 +93,13 @@ static bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
 
 	return true;
 }
+EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
 
 /*
  * Any packet that leaves via this function must come back
  * through nf_reinject().
  */
-static int __nf_queue(struct sk_buff *skb,
+int nf_queue(struct sk_buff *skb,
 		      struct nf_hook_ops *elem,
 		      u_int8_t pf, unsigned int hook,
 		      struct net_device *indev,
@@ -137,6 +139,7 @@ static int __nf_queue(struct sk_buff *skb,
 		.indev	= indev,
 		.outdev	= outdev,
 		.okfn	= okfn,
+		.size	= sizeof(*entry) + afinfo->route_key_size,
 	};
 
 	if (!nf_queue_entry_get_refs(entry)) {
@@ -163,87 +166,6 @@ err:
 	return status;
 }
 
-#ifdef CONFIG_BRIDGE_NETFILTER
-/* When called from bridge netfilter, skb->data must point to MAC header
- * before calling skb_gso_segment(). Else, original MAC header is lost
- * and segmented skbs will be sent to wrong destination.
- */
-static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
-{
-	if (skb->nf_bridge)
-		__skb_push(skb, skb->network_header - skb->mac_header);
-}
-
-static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
-{
-	if (skb->nf_bridge)
-		__skb_pull(skb, skb->network_header - skb->mac_header);
-}
-#else
-#define nf_bridge_adjust_skb_data(s) do {} while (0)
-#define nf_bridge_adjust_segmented_data(s) do {} while (0)
-#endif
-
-int nf_queue(struct sk_buff *skb,
-	     struct nf_hook_ops *elem,
-	     u_int8_t pf, unsigned int hook,
-	     struct net_device *indev,
-	     struct net_device *outdev,
-	     int (*okfn)(struct sk_buff *),
-	     unsigned int queuenum)
-{
-	struct sk_buff *segs;
-	int err = -EINVAL;
-	unsigned int queued;
-
-	if (!skb_is_gso(skb))
-		return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
-				  queuenum);
-
-	switch (pf) {
-	case NFPROTO_IPV4:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case NFPROTO_IPV6:
-		skb->protocol = htons(ETH_P_IPV6);
-		break;
-	}
-
-	nf_bridge_adjust_skb_data(skb);
-	segs = skb_gso_segment(skb, 0);
-	/* Does not use PTR_ERR to limit the number of error codes that can be
-	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to mean
-	 * 'ignore this hook'.
-	 */
-	if (IS_ERR(segs))
-		goto out_err;
-	queued = 0;
-	err = 0;
-	do {
-		struct sk_buff *nskb = segs->next;
-
-		segs->next = NULL;
-		if (err == 0) {
-			nf_bridge_adjust_segmented_data(segs);
-			err = __nf_queue(segs, elem, pf, hook, indev,
-					   outdev, okfn, queuenum);
-		}
-		if (err == 0)
-			queued++;
-		else
-			kfree_skb(segs);
-		segs = nskb;
-	} while (segs);
-
-	if (queued) {
-		kfree_skb(skb);
-		return 0;
-	}
-  out_err:
-	nf_bridge_adjust_segmented_data(skb);
-	return err;
-}
-
 void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
 {
 	struct sk_buff *skb = entry->skb;
@@ -283,9 +205,9 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
 		local_bh_enable();
 		break;
 	case NF_QUEUE:
-		err = __nf_queue(skb, elem, entry->pf, entry->hook,
-				 entry->indev, entry->outdev, entry->okfn,
-				 verdict >> NF_VERDICT_QBITS);
+		err = nf_queue(skb, elem, entry->pf, entry->hook,
+				entry->indev, entry->outdev, entry->okfn,
+				verdict >> NF_VERDICT_QBITS);
 		if (err < 0) {
 			if (err == -ECANCELED)
 				goto next_hook;
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index ef3cdb4..edbae4c 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -477,28 +477,13 @@ nla_put_failure:
 }
 
 static int
-nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
+__nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
+			struct nf_queue_entry *entry)
 {
 	struct sk_buff *nskb;
-	struct nfqnl_instance *queue;
 	int err = -ENOBUFS;
 	__be32 *packet_id_ptr;
 	int failopen = 0;
-	struct net *net = dev_net(entry->indev ?
-				  entry->indev : entry->outdev);
-	struct nfnl_queue_net *q = nfnl_queue_pernet(net);
-
-	/* rcu_read_lock()ed by nf_hook_slow() */
-	queue = instance_lookup(q, queuenum);
-	if (!queue) {
-		err = -ESRCH;
-		goto err_out;
-	}
-
-	if (queue->copy_mode == NFQNL_COPY_NONE) {
-		err = -EINVAL;
-		goto err_out;
-	}
 
 	nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
 	if (nskb == NULL) {
@@ -547,6 +532,141 @@ err_out:
 	return err;
 }
 
+static struct nf_queue_entry *
+nf_queue_entry_dup(struct nf_queue_entry *e)
+{
+	struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
+	if (entry) {
+		if (nf_queue_entry_get_refs(entry))
+			return entry;
+		kfree(entry);
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_BRIDGE_NETFILTER
+/* When called from bridge netfilter, skb->data must point to MAC header
+ * before calling skb_gso_segment(). Else, original MAC header is lost
+ * and segmented skbs will be sent to wrong destination.
+ */
+static void nf_bridge_adjust_skb_data(struct sk_buff *skb)
+{
+	if (skb->nf_bridge)
+		__skb_push(skb, skb->network_header - skb->mac_header);
+}
+
+static void nf_bridge_adjust_segmented_data(struct sk_buff *skb)
+{
+	if (skb->nf_bridge)
+		__skb_pull(skb, skb->network_header - skb->mac_header);
+}
+#else
+#define nf_bridge_adjust_skb_data(s) do {} while (0)
+#define nf_bridge_adjust_segmented_data(s) do {} while (0)
+#endif
+
+static void free_entry(struct nf_queue_entry *entry)
+{
+	nf_queue_entry_release_refs(entry);
+	kfree(entry);
+}
+
+static int
+__nfqnl_enqueue_packet_gso(struct net *net, struct nfqnl_instance *queue,
+			   struct sk_buff *skb, struct nf_queue_entry *entry)
+{
+	int ret = -ENOMEM;
+	struct nf_queue_entry *entry_seg;
+
+	nf_bridge_adjust_segmented_data(skb);
+
+	if (skb->next == NULL) { /* last packet, no need to copy entry */
+		struct sk_buff *gso_skb = entry->skb;
+		entry->skb = skb;
+		ret = __nfqnl_enqueue_packet(net, queue, entry);
+		if (ret)
+			entry->skb = gso_skb;
+		return ret;
+	}
+
+	skb->next = NULL;
+
+	entry_seg = nf_queue_entry_dup(entry);
+	if (entry_seg) {
+		entry_seg->skb = skb;
+		ret = __nfqnl_enqueue_packet(net, queue, entry_seg);
+		if (ret)
+			free_entry(entry_seg);
+	}
+	return ret;
+}
+
+static int
+nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
+{
+	unsigned int queued;
+	struct nfqnl_instance *queue;
+	struct sk_buff *skb, *segs;
+	int err = -ENOBUFS;
+	struct net *net = dev_net(entry->indev ?
+				  entry->indev : entry->outdev);
+	struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+
+	/* rcu_read_lock()ed by nf_hook_slow() */
+	queue = instance_lookup(q, queuenum);
+	if (!queue)
+		return -ESRCH;
+
+	if (queue->copy_mode == NFQNL_COPY_NONE)
+		return -EINVAL;
+
+	if (!skb_is_gso(entry->skb))
+		return __nfqnl_enqueue_packet(net, queue, entry);
+
+	skb = entry->skb;
+
+	switch (entry->pf) {
+	case NFPROTO_IPV4:
+		skb->protocol = htons(ETH_P_IP);
+		break;
+	case NFPROTO_IPV6:
+		skb->protocol = htons(ETH_P_IPV6);
+		break;
+	}
+
+	nf_bridge_adjust_skb_data(skb);
+	segs = skb_gso_segment(skb, 0);
+	/* Does not use PTR_ERR to limit the number of error codes that can be
+	 * returned by nf_queue.  For instance, callers rely on -ECANCELED to
+	 * mean 'ignore this hook'.
+	 */
+	if (IS_ERR(segs))
+		goto out_err;
+	queued = 0;
+	err = 0;
+	do {
+		struct sk_buff *nskb = segs->next;
+		if (err == 0)
+			err = __nfqnl_enqueue_packet_gso(net, queue,
+							segs, entry);
+		if (err == 0)
+			queued++;
+		else
+			kfree_skb(segs);
+		segs = nskb;
+	} while (segs);
+
+	if (queued) {
+		if (err) /* some segments are already queued */
+			free_entry(entry);
+		kfree_skb(skb);
+		return 0;
+	}
+ out_err:
+	nf_bridge_adjust_segmented_data(skb);
+	return err;
+}
+
 static int
 nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff)
 {
-- 
1.7.10.4

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

* [PATCH 16/18] netfilter: nfnetlink_queue: add skb info attribute
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (14 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 15/18] netfilter: move skb_gso_segment into nfnetlink_queue module Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 17/18] netfilter: nfnetlink_queue: avoid expensive gso segmentation and checksum fixup Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Florian Westphal <fw@strlen.de>

Once we allow userspace to receive gso/gro packets, userspace
needs to be able to determine when checksums appear to be
broken, but are not.

NFQA_SKB_CSUMNOTREADY means 'checksums will be fixed in kernel
later, pretend they are ok'.

NFQA_SKB_GSO could be used for statistics, or to determine when
packet size exceeds mtu.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/uapi/linux/netfilter/nfnetlink_queue.h |    7 +++++++
 net/netfilter/nfnetlink_queue_core.c           |   16 ++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h
index 70ec8c2..0069da3 100644
--- a/include/uapi/linux/netfilter/nfnetlink_queue.h
+++ b/include/uapi/linux/netfilter/nfnetlink_queue.h
@@ -45,6 +45,7 @@ enum nfqnl_attr_type {
 	NFQA_CT,			/* nf_conntrack_netlink.h */
 	NFQA_CT_INFO,			/* enum ip_conntrack_info */
 	NFQA_CAP_LEN,			/* __u32 length of captured packet */
+	NFQA_SKB_INFO,			/* __u32 skb meta information */
 
 	__NFQA_MAX
 };
@@ -98,4 +99,10 @@ enum nfqnl_attr_config {
 #define NFQA_CFG_F_CONNTRACK			(1 << 1)
 #define NFQA_CFG_F_MAX				(1 << 2)
 
+/* flags for NFQA_SKB_INFO */
+/* packet appears to have wrong checksums, but they are ok */
+#define NFQA_SKB_CSUMNOTREADY (1 << 0)
+/* packet is GSO (i.e., exceeds device mtu) */
+#define NFQA_SKB_GSO (1 << 1)
+
 #endif /* _NFNETLINK_QUEUE_H */
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index edbae4c..d052cd6 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -272,6 +272,18 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
 	skb_shinfo(to)->nr_frags = j;
 }
 
+static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet)
+{
+	__u32 flags = 0;
+
+	if (packet->ip_summed == CHECKSUM_PARTIAL)
+		flags = NFQA_SKB_CSUMNOTREADY;
+	if (skb_is_gso(packet))
+		flags |= NFQA_SKB_GSO;
+
+	return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0;
+}
+
 static struct sk_buff *
 nfqnl_build_packet_message(struct nfqnl_instance *queue,
 			   struct nf_queue_entry *entry,
@@ -301,6 +313,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
 #endif
 		+ nla_total_size(sizeof(u_int32_t))	/* mark */
 		+ nla_total_size(sizeof(struct nfqnl_msg_packet_hw))
+		+ nla_total_size(sizeof(u_int32_t))	/* skbinfo */
 		+ nla_total_size(sizeof(u_int32_t));	/* cap_len */
 
 	if (entskb->tstamp.tv64)
@@ -454,6 +467,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
 	if (cap_len > 0 && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
 		goto nla_put_failure;
 
+	if (nfqnl_put_packet_info(skb, entskb))
+		goto nla_put_failure;
+
 	if (data_len) {
 		struct nlattr *nla;
 
-- 
1.7.10.4


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

* [PATCH 17/18] netfilter: nfnetlink_queue: avoid expensive gso segmentation and checksum fixup
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (15 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 16/18] netfilter: nfnetlink_queue: add skb info attribute Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 18:22 ` [PATCH 18/18] sctp: Correct type and usage of sctp_end_cksum() Pablo Neira Ayuso
  2013-04-29 19:17 ` [PATCH 00/18] netfilter updates for net-next (try 2) David Miller
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Florian Westphal <fw@strlen.de>

Userspace can now indicate that it can cope with larger-than-mtu sized
packets and packets that have invalid ipv4/tcp checksums.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/uapi/linux/netfilter/nfnetlink_queue.h |    3 ++-
 net/netfilter/nfnetlink_queue_core.c           |    5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h
index 0069da3..a2308ae 100644
--- a/include/uapi/linux/netfilter/nfnetlink_queue.h
+++ b/include/uapi/linux/netfilter/nfnetlink_queue.h
@@ -97,7 +97,8 @@ enum nfqnl_attr_config {
 /* Flags for NFQA_CFG_FLAGS */
 #define NFQA_CFG_F_FAIL_OPEN			(1 << 0)
 #define NFQA_CFG_F_CONNTRACK			(1 << 1)
-#define NFQA_CFG_F_MAX				(1 << 2)
+#define NFQA_CFG_F_GSO				(1 << 2)
+#define NFQA_CFG_F_MAX				(1 << 3)
 
 /* flags for NFQA_SKB_INFO */
 /* packet appears to have wrong checksums, but they are ok */
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index d052cd6..2e0e835 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -327,7 +327,8 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
 		break;
 
 	case NFQNL_COPY_PACKET:
-		if (entskb->ip_summed == CHECKSUM_PARTIAL &&
+		if (!(queue->flags & NFQA_CFG_F_GSO) &&
+		    entskb->ip_summed == CHECKSUM_PARTIAL &&
 		    skb_checksum_help(entskb))
 			return NULL;
 
@@ -636,7 +637,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
 	if (queue->copy_mode == NFQNL_COPY_NONE)
 		return -EINVAL;
 
-	if (!skb_is_gso(entry->skb))
+	if ((queue->flags & NFQA_CFG_F_GSO) || !skb_is_gso(entry->skb))
 		return __nfqnl_enqueue_packet(net, queue, entry);
 
 	skb = entry->skb;
-- 
1.7.10.4

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

* [PATCH 18/18] sctp: Correct type and usage of sctp_end_cksum()
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (16 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 17/18] netfilter: nfnetlink_queue: avoid expensive gso segmentation and checksum fixup Pablo Neira Ayuso
@ 2013-04-29 18:22 ` Pablo Neira Ayuso
  2013-04-29 19:17 ` [PATCH 00/18] netfilter updates for net-next (try 2) David Miller
  18 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-29 18:22 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Simon Horman <horms@verge.net.au>

Change the type of the crc32 parameter of sctp_end_cksum()
from __be32 to __u32 to reflect that fact that it is passed
to cpu_to_le32().

There are five in-tree users of sctp_end_cksum().
The following four had warnings flagged by sparse which are
no longer present with this change.

net/netfilter/ipvs/ip_vs_proto_sctp.c:sctp_nat_csum()
net/netfilter/ipvs/ip_vs_proto_sctp.c:sctp_csum_check()
net/sctp/input.c:sctp_rcv_checksum()
net/sctp/output.c:sctp_packet_transmit()

The fifth user is net/netfilter/nf_nat_proto_sctp.c:sctp_manip_pkt().
It has been updated to pass a __u32 instead of a __be32,
the value in question was already calculated in cpu byte-order.

net/netfilter/nf_nat_proto_sctp.c:sctp_manip_pkt() has also
been updated to assign the return value of sctp_end_cksum()
directly to a variable of type __le32, matching the
type of the return value. Previously the return value
was assigned to a variable of type __be32 and then that variable
was finally assigned to another variable of type __le32.

Problems flagged by sparse.
Compile and sparse tested only.

Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/net/sctp/checksum.h       |    2 +-
 net/netfilter/nf_nat_proto_sctp.c |    5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h
index befc8d2..5a2110d 100644
--- a/include/net/sctp/checksum.h
+++ b/include/net/sctp/checksum.h
@@ -77,7 +77,7 @@ static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
 	return sctp_crc32c(crc32, buffer, length);
 }
 
-static inline __le32 sctp_end_cksum(__be32 crc32)
+static inline __le32 sctp_end_cksum(__u32 crc32)
 {
 	return cpu_to_le32(~crc32);
 }
diff --git a/net/netfilter/nf_nat_proto_sctp.c b/net/netfilter/nf_nat_proto_sctp.c
index e64faa5..396e55d 100644
--- a/net/netfilter/nf_nat_proto_sctp.c
+++ b/net/netfilter/nf_nat_proto_sctp.c
@@ -36,7 +36,7 @@ sctp_manip_pkt(struct sk_buff *skb,
 {
 	struct sk_buff *frag;
 	sctp_sctphdr_t *hdr;
-	__be32 crc32;
+	__u32 crc32;
 
 	if (!skb_make_writable(skb, hdroff + sizeof(*hdr)))
 		return false;
@@ -55,8 +55,7 @@ sctp_manip_pkt(struct sk_buff *skb,
 	skb_walk_frags(skb, frag)
 		crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag),
 					  crc32);
-	crc32 = sctp_end_cksum(crc32);
-	hdr->checksum = crc32;
+	hdr->checksum = sctp_end_cksum(crc32);
 
 	return true;
 }
-- 
1.7.10.4


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

* Re: [PATCH 00/18] netfilter updates for net-next (try 2)
  2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
                   ` (17 preceding siblings ...)
  2013-04-29 18:22 ` [PATCH 18/18] sctp: Correct type and usage of sctp_end_cksum() Pablo Neira Ayuso
@ 2013-04-29 19:17 ` David Miller
  18 siblings, 0 replies; 23+ messages in thread
From: David Miller @ 2013-04-29 19:17 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, netdev

From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon, 29 Apr 2013 20:22:06 +0200

> This is a second pull request resolving the compilation issue in
> net/sched/em_ipset.
> 
> The following patchset contains relevant updates for the Netfilter
> tree, they are:
> 
> * Enhancements for ipset: Add the counter extension for sets, this
>   information can be used from the iptables set match, to change
>   the matching behaviour. Jozsef required to add the extension
>   infrastructure and moved the existing timeout support upon it.
>   This also includes a change in net/sched/em_ipset to adapt it to
>   the new extension structure.
> 
> * Enhancements for performance boosting in nfnetlink_queue: Add new
>   configuration flags that allows user-space to receive big packets (GRO)
>   and to disable checksumming calculation. This were proposed by Eric
>   Dumazet during the Netfilter Workshop 2013 in Copenhagen. Florian
>   Westphal was kind enough to find the time to materialize the proposal.
> 
> * A sparse fix from Simon, he noticed it in the SCTP NAT helper, the fix
>   required a change in the interface of sctp_end_cksum.
 ...
>   git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master

Pulled, thanks.

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

* RE: [PATCH 04/18] netfilter: ipset: Unified bitmap type generation
  2013-04-29 18:22 ` [PATCH 04/18] netfilter: ipset: Unified bitmap type generation Pablo Neira Ayuso
@ 2013-04-30  8:49   ` David Laight
  2013-04-30  9:19     ` Jozsef Kadlecsik
  0 siblings, 1 reply; 23+ messages in thread
From: David Laight @ 2013-04-30  8:49 UTC (permalink / raw)
  To: Pablo Neira Ayuso, netfilter-devel; +Cc: davem, netdev

> +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
> @@ -0,0 +1,265 @@
...
> +#define CONCAT(a, b)		a##b
> +#define TOKEN(a,b)		CONCAT(a, b)
> +
> +#define mtype_do_test		TOKEN(MTYPE, _do_test)
> +#define mtype_gc_test		TOKEN(MTYPE, _gc_test)
...
> +#define mtype			MTYPE

I'm not entirely sure what is being achieved by the above.
Either there ought to be a comment about what MTYPE might
be set to, or it is all pointless.

In any case names like CONCAT() and TOKEN() are likely
to be double definitions.

A similar namespace issue arises with REVISION_MIN/MAX.

	David

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

* RE: [PATCH 04/18] netfilter: ipset: Unified bitmap type generation
  2013-04-30  8:49   ` David Laight
@ 2013-04-30  9:19     ` Jozsef Kadlecsik
  0 siblings, 0 replies; 23+ messages in thread
From: Jozsef Kadlecsik @ 2013-04-30  9:19 UTC (permalink / raw)
  To: David Laight; +Cc: Pablo Neira Ayuso, netfilter-devel, David Miller, netdev

On Tue, 30 Apr 2013, David Laight wrote:

> > +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
> > @@ -0,0 +1,265 @@
> ...
> > +#define CONCAT(a, b)		a##b
> > +#define TOKEN(a,b)		CONCAT(a, b)
> > +
> > +#define mtype_do_test		TOKEN(MTYPE, _do_test)
> > +#define mtype_gc_test		TOKEN(MTYPE, _gc_test)
> ...
> > +#define mtype			MTYPE
> 
> I'm not entirely sure what is being achieved by the above.
> Either there ought to be a comment about what MTYPE might
> be set to, or it is all pointless.

mtype is defined in order to SHOUT less in the code later.
 
> In any case names like CONCAT() and TOKEN() are likely
> to be double definitions.
>
> A similar namespace issue arises with REVISION_MIN/MAX.

I'll send a patch with more speficic names.

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] 23+ messages in thread

* [PATCH 08/18] netfilter: ipset: list:set type using the extension interface
  2013-04-27 18:58 [PATCH 00/18] netfilter updates for net-next Pablo Neira Ayuso
@ 2013-04-27 18:58 ` Pablo Neira Ayuso
  0 siblings, 0 replies; 23+ messages in thread
From: Pablo Neira Ayuso @ 2013-04-27 18:58 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev

From: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
---
 net/netfilter/ipset/ip_set_list_set.c |  547 ++++++++++++++++++---------------
 1 file changed, 301 insertions(+), 246 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 09c744a..919eefe 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2008-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -13,7 +13,6 @@
 #include <linux/errno.h>
 
 #include <linux/netfilter/ipset/ip_set.h>
-#include <linux/netfilter/ipset/ip_set_timeout.h>
 #include <linux/netfilter/ipset/ip_set_list.h>
 
 #define REVISION_MIN	0
@@ -24,19 +23,28 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
 IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
 MODULE_ALIAS("ip_set_list:set");
 
-/* Member elements without and with timeout */
+/* Member elements  */
 struct set_elem {
 	ip_set_id_t id;
 };
 
-struct set_telem {
-	ip_set_id_t id;
+struct sett_elem {
+	struct {
+		ip_set_id_t id;
+	} __attribute__ ((aligned));
 	unsigned long timeout;
 };
 
+struct set_adt_elem {
+	ip_set_id_t id;
+	ip_set_id_t refid;
+	int before;
+};
+
 /* Type structure */
 struct list_set {
 	size_t dsize;		/* element size */
+	size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */
 	u32 size;		/* size of set list array */
 	u32 timeout;		/* timeout value */
 	struct timer_list gc;	/* garbage collection */
@@ -49,179 +57,296 @@ list_set_elem(const struct list_set *map, u32 id)
 	return (struct set_elem *)((void *)map->members + id * map->dsize);
 }
 
-static inline struct set_telem *
-list_set_telem(const struct list_set *map, u32 id)
-{
-	return (struct set_telem *)((void *)map->members + id * map->dsize);
-}
+#define ext_timeout(e, m)	\
+(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
 
-static inline bool
-list_set_timeout(const struct list_set *map, u32 id)
+static int
+list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
+	       const struct xt_action_param *par,
+	       struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
-	const struct set_telem *elem = list_set_telem(map, id);
+	struct list_set *map = set->data;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	return ip_set_timeout_test(elem->timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return 0;
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		ret = ip_set_test(e->id, skb, par, opt);
+		if (ret > 0)
+			return ret;
+	}
+	return 0;
 }
 
-static inline bool
-list_set_expired(const struct list_set *map, u32 id)
+static int
+list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
+	      const struct xt_action_param *par,
+	      struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
-	const struct set_telem *elem = list_set_telem(map, id);
+	struct list_set *map = set->data;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	return ip_set_timeout_expired(elem->timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return 0;
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		ret = ip_set_add(e->id, skb, par, opt);
+		if (ret == 0)
+			return ret;
+	}
+	return 0;
 }
 
-/* Set list without and with timeout */
-
 static int
-list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
+list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
 	      const struct xt_action_param *par,
-	      enum ipset_adt adt, const struct ip_set_adt_opt *opt)
+	      struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 {
 	struct list_set *map = set->data;
-	struct set_elem *elem;
+	struct set_elem *e;
 	u32 i;
 	int ret;
 
 	for (i = 0; i < map->size; i++) {
-		elem = list_set_elem(map, i);
-		if (elem->id == IPSET_INVALID_ID)
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
 			return 0;
-		if (with_timeout(map->timeout) && list_set_expired(map, i))
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
 			continue;
-		switch (adt) {
-		case IPSET_TEST:
-			ret = ip_set_test(elem->id, skb, par, opt);
-			if (ret > 0)
-				return ret;
-			break;
-		case IPSET_ADD:
-			ret = ip_set_add(elem->id, skb, par, opt);
-			if (ret == 0)
-				return ret;
-			break;
-		case IPSET_DEL:
-			ret = ip_set_del(elem->id, skb, par, opt);
-			if (ret == 0)
-				return ret;
-			break;
-		default:
-			break;
-		}
+		ret = ip_set_del(e->id, skb, par, opt);
+		if (ret == 0)
+			return ret;
+	}
+	return 0;
+}
+
+static int
+list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
+	      const struct xt_action_param *par,
+	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
+{
+	struct list_set *map = set->data;
+	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map);
+
+	switch (adt) {
+	case IPSET_TEST:
+		return list_set_ktest(set, skb, par, opt, &ext);
+	case IPSET_ADD:
+		return list_set_kadd(set, skb, par, opt, &ext);
+	case IPSET_DEL:
+		return list_set_kdel(set, skb, par, opt, &ext);
+	default:
+		break;
 	}
 	return -EINVAL;
 }
 
 static bool
-id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
+id_eq(const struct ip_set *set, u32 i, ip_set_id_t id)
 {
-	const struct set_elem *elem;
+	const struct list_set *map = set->data;
+	const struct set_elem *e;
+
+	if (i >= map->size)
+		return 0;
 
-	if (i < map->size) {
-		elem = list_set_elem(map, i);
-		return elem->id == id;
+	e = list_set_elem(map, i);
+	return !!(e->id == id &&
+		 !(SET_WITH_TIMEOUT(set) &&
+		   ip_set_timeout_expired(ext_timeout(e, map))));
+}
+
+static int
+list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d,
+	     const struct ip_set_ext *ext)
+{
+	struct list_set *map = set->data;
+	struct set_elem *e = list_set_elem(map, i);
+
+	if (e->id != IPSET_INVALID_ID) {
+		if (i == map->size - 1)
+			/* Last element replaced: e.g. add new,before,last */
+			ip_set_put_byindex(e->id);
+		else {
+			struct set_elem *x = list_set_elem(map, map->size - 1);
+
+			/* Last element pushed off */
+			if (x->id != IPSET_INVALID_ID)
+				ip_set_put_byindex(x->id);
+			memmove(list_set_elem(map, i + 1), e,
+				map->dsize * (map->size - (i + 1)));
+		}
 	}
 
+	e->id = d->id;
+	if (SET_WITH_TIMEOUT(set))
+		ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
 	return 0;
 }
 
-static bool
-id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
+static int
+list_set_del(struct ip_set *set, u32 i)
 {
-	const struct set_elem *elem;
+	struct list_set *map = set->data;
+	struct set_elem *e = list_set_elem(map, i);
 
-	if (i < map->size) {
-		elem = list_set_elem(map, i);
-		return !!(elem->id == id &&
-			  !(with_timeout(map->timeout) &&
-			    list_set_expired(map, i)));
-	}
+	ip_set_put_byindex(e->id);
 
+	if (i < map->size - 1)
+		memmove(e, list_set_elem(map, i + 1),
+			map->dsize * (map->size - (i + 1)));
+
+	/* Last element */
+	e = list_set_elem(map, map->size - 1);
+	e->id = IPSET_INVALID_ID;
 	return 0;
 }
 
 static void
-list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
+set_cleanup_entries(struct ip_set *set)
 {
+	struct list_set *map = set->data;
 	struct set_elem *e;
+	u32 i;
 
-	for (; i < map->size; i++) {
+	for (i = 0; i < map->size; i++) {
 		e = list_set_elem(map, i);
-		swap(e->id, id);
-		if (e->id == IPSET_INVALID_ID)
-			break;
+		if (e->id != IPSET_INVALID_ID &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
+			list_set_del(set, i);
 	}
 }
 
-static void
-list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
-	       unsigned long timeout)
+static int
+list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	       struct ip_set_ext *mext, u32 flags)
 {
-	struct set_telem *e;
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
+	u32 i;
+	int ret;
 
-	for (; i < map->size; i++) {
-		e = list_set_telem(map, i);
-		swap(e->id, id);
-		swap(e->timeout, timeout);
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
 		if (e->id == IPSET_INVALID_ID)
-			break;
+			return 0;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
+
+		if (d->before == 0)
+			return 1;
+		else if (d->before > 0)
+			ret = id_eq(set, i + 1, d->refid);
+		else
+			ret = i > 0 && id_eq(set, i - 1, d->refid);
+		return ret;
 	}
+	return 0;
 }
 
+
 static int
-list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
-	     unsigned long timeout)
+list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	      struct ip_set_ext *mext, u32 flags)
 {
-	const struct set_elem *e = list_set_elem(map, i);
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
+	bool flag_exist = flags & IPSET_FLAG_EXIST;
+	u32 i, ret = 0;
 
-	if (e->id != IPSET_INVALID_ID) {
-		const struct set_elem *x = list_set_elem(map, map->size - 1);
+	/* Check already added element */
+	for (i = 0; i < map->size; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			goto insert;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
 
-		/* Last element replaced or pushed off */
-		if (x->id != IPSET_INVALID_ID)
-			ip_set_put_byindex(x->id);
+		if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) ||
+		    (d->before < 0 &&
+		     (i == 0 || !id_eq(set, i - 1, d->refid))))
+			/* Before/after doesn't match */
+			return -IPSET_ERR_REF_EXIST;
+		if (!flag_exist)
+			/* Can't re-add */
+			return -IPSET_ERR_EXIST;
+		/* Update extensions */
+		if (SET_WITH_TIMEOUT(set))
+			ip_set_timeout_set(ext_timeout(e, map), ext->timeout);
+		/* Set is already added to the list */
+		ip_set_put_byindex(d->id);
+		return 0;
+	}
+insert:
+	ret = -IPSET_ERR_LIST_FULL;
+	for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
+				: list_set_add(set, i, d, ext);
+		else if (e->id != d->refid)
+			continue;
+		else if (d->before > 0)
+			ret = list_set_add(set, i, d, ext);
+		else if (i + 1 < map->size)
+			ret = list_set_add(set, i + 1, d, ext);
 	}
-	if (with_timeout(map->timeout))
-		list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
-	else
-		list_elem_add(map, i, id);
 
-	return 0;
+	return ret;
 }
 
 static int
-list_set_del(struct list_set *map, u32 i)
+list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+	      struct ip_set_ext *mext, u32 flags)
 {
-	struct set_elem *a = list_set_elem(map, i), *b;
-
-	ip_set_put_byindex(a->id);
-
-	for (; i < map->size - 1; i++) {
-		b = list_set_elem(map, i + 1);
-		a->id = b->id;
-		if (with_timeout(map->timeout))
-			((struct set_telem *)a)->timeout =
-				((struct set_telem *)b)->timeout;
-		a = b;
-		if (a->id == IPSET_INVALID_ID)
-			break;
-	}
-	/* Last element */
-	a->id = IPSET_INVALID_ID;
-	return 0;
-}
-
-static void
-cleanup_entries(struct list_set *map)
-{
-	struct set_telem *e;
+	struct list_set *map = set->data;
+	struct set_adt_elem *d = value;
+	struct set_elem *e;
 	u32 i;
 
 	for (i = 0; i < map->size; i++) {
-		e = list_set_telem(map, i);
-		if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
-			list_set_del(map, i);
+		e = list_set_elem(map, i);
+		if (e->id == IPSET_INVALID_ID)
+			return d->before != 0 ? -IPSET_ERR_REF_EXIST
+					      : -IPSET_ERR_EXIST;
+		else if (SET_WITH_TIMEOUT(set) &&
+			 ip_set_timeout_expired(ext_timeout(e, map)))
+			continue;
+		else if (e->id != d->id)
+			continue;
+
+		if (d->before == 0)
+			return list_set_del(set, i);
+		else if (d->before > 0) {
+			if (!id_eq(set, i + 1, d->refid))
+				return -IPSET_ERR_REF_EXIST;
+			return list_set_del(set, i);
+		} else if (i == 0 || !id_eq(set, i - 1, d->refid))
+			return -IPSET_ERR_REF_EXIST;
+		else
+			return list_set_del(set, i);
 	}
+	return -IPSET_ERR_EXIST;
 }
 
 static int
@@ -229,14 +354,10 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 {
 	struct list_set *map = set->data;
-	bool with_timeout = with_timeout(map->timeout);
-	bool flag_exist = flags & IPSET_FLAG_EXIST;
-	int before = 0;
-	u32 timeout = map->timeout;
-	ip_set_id_t id, refid = IPSET_INVALID_ID;
-	const struct set_elem *elem;
+	ipset_adtfn adtfn = set->variant->adt[adt];
+	struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
+	struct ip_set_ext ext = IP_SET_INIT_UEXT(map);
 	struct ip_set *s;
-	u32 i;
 	int ret = 0;
 
 	if (unlikely(!tb[IPSET_ATTR_NAME] ||
@@ -247,8 +368,11 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 	if (tb[IPSET_ATTR_LINENO])
 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 
-	id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
-	if (id == IPSET_INVALID_ID)
+	ret = ip_set_get_extensions(set, tb, &ext);
+	if (ret)
+		return ret;
+	e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
+	if (e.id == IPSET_INVALID_ID)
 		return -IPSET_ERR_NAME;
 	/* "Loop detection" */
 	if (s->type->features & IPSET_TYPE_NAME) {
@@ -258,115 +382,34 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 
 	if (tb[IPSET_ATTR_CADT_FLAGS]) {
 		u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
-		before = f & IPSET_FLAG_BEFORE;
+		e.before = f & IPSET_FLAG_BEFORE;
 	}
 
-	if (before && !tb[IPSET_ATTR_NAMEREF]) {
+	if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
 		ret = -IPSET_ERR_BEFORE;
 		goto finish;
 	}
 
 	if (tb[IPSET_ATTR_NAMEREF]) {
-		refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
-					  &s);
-		if (refid == IPSET_INVALID_ID) {
+		e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
+					    &s);
+		if (e.refid == IPSET_INVALID_ID) {
 			ret = -IPSET_ERR_NAMEREF;
 			goto finish;
 		}
-		if (!before)
-			before = -1;
-	}
-	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!with_timeout) {
-			ret = -IPSET_ERR_TIMEOUT;
-			goto finish;
-		}
-		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+		if (!e.before)
+			e.before = -1;
 	}
-	if (with_timeout && adt != IPSET_TEST)
-		cleanup_entries(map);
+	if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
+		set_cleanup_entries(set);
 
-	switch (adt) {
-	case IPSET_TEST:
-		for (i = 0; i < map->size && !ret; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID ||
-			    (before != 0 && i + 1 >= map->size))
-				break;
-			else if (with_timeout && list_set_expired(map, i))
-				continue;
-			else if (before > 0 && elem->id == id)
-				ret = id_eq_timeout(map, i + 1, refid);
-			else if (before < 0 && elem->id == refid)
-				ret = id_eq_timeout(map, i + 1, id);
-			else if (before == 0 && elem->id == id)
-				ret = 1;
-		}
-		break;
-	case IPSET_ADD:
-		for (i = 0; i < map->size; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id != id)
-				continue;
-			if (!(with_timeout && flag_exist)) {
-				ret = -IPSET_ERR_EXIST;
-				goto finish;
-			} else {
-				struct set_telem *e = list_set_telem(map, i);
-
-				if ((before > 1 &&
-				     !id_eq(map, i + 1, refid)) ||
-				    (before < 0 &&
-				     (i == 0 || !id_eq(map, i - 1, refid)))) {
-					ret = -IPSET_ERR_EXIST;
-					goto finish;
-				}
-				e->timeout = ip_set_timeout_set(timeout);
-				ip_set_put_byindex(id);
-				ret = 0;
-				goto finish;
-			}
-		}
-		ret = -IPSET_ERR_LIST_FULL;
-		for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID)
-				ret = before != 0 ? -IPSET_ERR_REF_EXIST
-					: list_set_add(map, i, id, timeout);
-			else if (elem->id != refid)
-				continue;
-			else if (before > 0)
-				ret = list_set_add(map, i, id, timeout);
-			else if (i + 1 < map->size)
-				ret = list_set_add(map, i + 1, id, timeout);
-		}
-		break;
-	case IPSET_DEL:
-		ret = -IPSET_ERR_EXIST;
-		for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
-			elem = list_set_elem(map, i);
-			if (elem->id == IPSET_INVALID_ID) {
-				ret = before != 0 ? -IPSET_ERR_REF_EXIST
-						  : -IPSET_ERR_EXIST;
-				break;
-			} else if (elem->id == id &&
-				   (before == 0 ||
-				    (before > 0 && id_eq(map, i + 1, refid))))
-				ret = list_set_del(map, i);
-			else if (elem->id == refid &&
-				 before < 0 && id_eq(map, i + 1, id))
-				ret = list_set_del(map, i + 1);
-		}
-		break;
-	default:
-		break;
-	}
+	ret = adtfn(set, &e, &ext, &ext, flags);
 
 finish:
-	if (refid != IPSET_INVALID_ID)
-		ip_set_put_byindex(refid);
+	if (e.refid != IPSET_INVALID_ID)
+		ip_set_put_byindex(e.refid);
 	if (adt != IPSET_ADD || ret)
-		ip_set_put_byindex(id);
+		ip_set_put_byindex(e.id);
 
 	return ip_set_eexist(ret, flags) ? 0 : ret;
 }
@@ -375,14 +418,14 @@ static void
 list_set_flush(struct ip_set *set)
 {
 	struct list_set *map = set->data;
-	struct set_elem *elem;
+	struct set_elem *e;
 	u32 i;
 
 	for (i = 0; i < map->size; i++) {
-		elem = list_set_elem(map, i);
-		if (elem->id != IPSET_INVALID_ID) {
-			ip_set_put_byindex(elem->id);
-			elem->id = IPSET_INVALID_ID;
+		e = list_set_elem(map, i);
+		if (e->id != IPSET_INVALID_ID) {
+			ip_set_put_byindex(e->id);
+			e->id = IPSET_INVALID_ID;
 		}
 	}
 }
@@ -392,7 +435,7 @@ list_set_destroy(struct ip_set *set)
 {
 	struct list_set *map = set->data;
 
-	if (with_timeout(map->timeout))
+	if (SET_WITH_TIMEOUT(set))
 		del_timer_sync(&map->gc);
 	list_set_flush(set);
 	kfree(map);
@@ -410,7 +453,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
 	if (!nested)
 		goto nla_put_failure;
 	if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
-	    (with_timeout(map->timeout) &&
+	    (SET_WITH_TIMEOUT(set) &&
 	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
 	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
 	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
@@ -440,7 +483,8 @@ list_set_list(const struct ip_set *set,
 		e = list_set_elem(map, i);
 		if (e->id == IPSET_INVALID_ID)
 			goto finish;
-		if (with_timeout(map->timeout) && list_set_expired(map, i))
+		if (SET_WITH_TIMEOUT(set) &&
+		    ip_set_timeout_expired(ext_timeout(e, map)))
 			continue;
 		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 		if (!nested) {
@@ -453,13 +497,11 @@ list_set_list(const struct ip_set *set,
 		if (nla_put_string(skb, IPSET_ATTR_NAME,
 				   ip_set_name_byindex(e->id)))
 			goto nla_put_failure;
-		if (with_timeout(map->timeout)) {
-			const struct set_telem *te =
-				(const struct set_telem *) e;
-			__be32 to = htonl(ip_set_timeout_get(te->timeout));
-			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
-				goto nla_put_failure;
-		}
+		if (SET_WITH_TIMEOUT(set) &&
+		    nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+				  htonl(ip_set_timeout_get(
+						ext_timeout(e, map)))))
+			goto nla_put_failure;
 		ipset_nest_end(skb, nested);
 	}
 finish:
@@ -485,12 +527,18 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b)
 	const struct list_set *y = b->data;
 
 	return x->size == y->size &&
-	       x->timeout == y->timeout;
+	       x->timeout == y->timeout &&
+	       a->extensions == b->extensions;
 }
 
-static const struct ip_set_type_variant list_set = {
+static const struct ip_set_type_variant set_variant = {
 	.kadt	= list_set_kadt,
 	.uadt	= list_set_uadt,
+	.adt	= {
+		[IPSET_ADD] = list_set_uadd,
+		[IPSET_DEL] = list_set_udel,
+		[IPSET_TEST] = list_set_utest,
+	},
 	.destroy = list_set_destroy,
 	.flush	= list_set_flush,
 	.head	= list_set_head,
@@ -505,7 +553,7 @@ list_set_gc(unsigned long ul_set)
 	struct list_set *map = set->data;
 
 	write_lock_bh(&set->lock);
-	cleanup_entries(map);
+	set_cleanup_entries(set);
 	write_unlock_bh(&set->lock);
 
 	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
@@ -513,20 +561,20 @@ list_set_gc(unsigned long ul_set)
 }
 
 static void
-list_set_gc_init(struct ip_set *set)
+list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
 {
 	struct list_set *map = set->data;
 
 	init_timer(&map->gc);
 	map->gc.data = (unsigned long) set;
-	map->gc.function = list_set_gc;
+	map->gc.function = gc;
 	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
 	add_timer(&map->gc);
 }
 
 /* Create list:set type of sets */
 
-static bool
+static struct list_set *
 init_list_set(struct ip_set *set, u32 size, size_t dsize,
 	      unsigned long timeout)
 {
@@ -536,7 +584,7 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
 
 	map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
 	if (!map)
-		return false;
+		return NULL;
 
 	map->size = size;
 	map->dsize = dsize;
@@ -548,13 +596,15 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize,
 		e->id = IPSET_INVALID_ID;
 	}
 
-	return true;
+	return map;
 }
 
 static int
 list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
+	struct list_set *map;
 	u32 size = IP_SET_LIST_DEFAULT_SIZE;
+	unsigned long timeout = 0;
 
 	if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
 		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
@@ -565,18 +615,23 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 	if (size < IP_SET_LIST_MIN_SIZE)
 		size = IP_SET_LIST_MIN_SIZE;
 
+	if (tb[IPSET_ATTR_TIMEOUT])
+		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
+	set->variant = &set_variant;
 	if (tb[IPSET_ATTR_TIMEOUT]) {
-		if (!init_list_set(set, size, sizeof(struct set_telem),
-				   ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
+		map = init_list_set(set, size,
+				    sizeof(struct sett_elem), timeout);
+		if (!map)
 			return -ENOMEM;
-
-		list_set_gc_init(set);
+		set->extensions |= IPSET_EXT_TIMEOUT;
+		map->offset[IPSET_OFFSET_TIMEOUT] =
+			offsetof(struct sett_elem, timeout);
+		list_set_gc_init(set, list_set_gc);
 	} else {
-		if (!init_list_set(set, size, sizeof(struct set_elem),
-				   IPSET_NO_TIMEOUT))
+		map = init_list_set(set, size, sizeof(struct set_elem), 0);
+		if (!map)
 			return -ENOMEM;
 	}
-	set->variant = &list_set;
 	return 0;
 }
 
-- 
1.7.10.4


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

end of thread, other threads:[~2013-04-30  9:19 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-04-29 18:22 [PATCH 00/18] netfilter updates for net-next (try 2) Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 01/18] netfilter: ipset: Make possible to test elements marked with nomatch Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 02/18] netfilter: ipset: Move often used IPv6 address masking function to header file Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 03/18] netfilter: ipset: Introduce extensions to elements in the core Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 04/18] netfilter: ipset: Unified bitmap type generation Pablo Neira Ayuso
2013-04-30  8:49   ` David Laight
2013-04-30  9:19     ` Jozsef Kadlecsik
2013-04-29 18:22 ` [PATCH 05/18] netfilter: ipset: Bitmap types using the unified code base Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 06/18] netfilter: ipset: Unified hash type generation Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 07/18] netfilter: ipset: Hash types using the unified code base Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 08/18] netfilter: ipset: list:set type using the extension interface Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 09/18] netfilter: ipset: Introduce the counter extension in the core Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 10/18] netfilter: ipset: The bitmap types with counter support Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 11/18] netfilter: ipset: The hash " Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 12/18] netfilter: ipset: The list:set type " Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 13/18] netfilter: ipset: set match: add support to match the counters Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 14/18] netfilter: nf_queue: move device refcount bump to extra function Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 15/18] netfilter: move skb_gso_segment into nfnetlink_queue module Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 16/18] netfilter: nfnetlink_queue: add skb info attribute Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 17/18] netfilter: nfnetlink_queue: avoid expensive gso segmentation and checksum fixup Pablo Neira Ayuso
2013-04-29 18:22 ` [PATCH 18/18] sctp: Correct type and usage of sctp_end_cksum() Pablo Neira Ayuso
2013-04-29 19:17 ` [PATCH 00/18] netfilter updates for net-next (try 2) David Miller
  -- strict thread matches above, loose matches on Subject: below --
2013-04-27 18:58 [PATCH 00/18] netfilter updates for net-next Pablo Neira Ayuso
2013-04-27 18:58 ` [PATCH 08/18] netfilter: ipset: list:set type using the extension interface Pablo Neira Ayuso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).