[nftables libnl] libnl: nf_tables: add support for set API

Patrick McHardy netfilter-cvslog-bounces at lists.netfilter.org
Wed Apr 8 06:20:42 CEST 2009


Gitweb:		http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libnl-nft.git;a=commit;h=0917b23733f46c65703545f82cc633836436f13b
commit 0917b23733f46c65703545f82cc633836436f13b
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Wed Apr 8 06:19:09 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Wed Apr 8 06:19:09 2009 +0200

    libnl: nf_tables: add support for set API
    
    Add support for the new set API to maintain sets independantly of the ruleset.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>
       via  0917b23733f46c65703545f82cc633836436f13b (commit)
      from  faa013b79cf0a723c78bad6f61e679fcb61f110f (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 0917b23733f46c65703545f82cc633836436f13b
Author: Patrick McHardy <kaber at trash.net>
Date:   Wed Apr 8 06:19:09 2009 +0200

    libnl: nf_tables: add support for set API
    
    Add support for the new set API to maintain sets independantly of the ruleset.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

-----------------------------------------------------------------------

 include/linux/netfilter/nf_tables.h     |  374 +++++++++++++++++++++++++-----
 include/netlink-types.h                 |   22 ++
 include/netlink/netfilter/nft_expr.h    |   78 +------
 include/netlink/netfilter/nft_set.h     |   90 +++++++
 include/netlink/netfilter/nft_setelem.h |   63 +++++
 lib/netfilter/nft_hash_expr.c           |  307 ------------------------
 lib/netfilter/nft_lookup_expr.c         |  179 ++++++++++++++
 lib/netfilter/nft_set.c                 |  321 +++++++++++++++++++++++++
 lib/netfilter/nft_set_expr.c            |  372 -----------------------------
 lib/netfilter/nft_set_obj.c             |  346 +++++++++++++++++++++++++++
 lib/netfilter/nft_setelem.c             |  391 +++++++++++++++++++++++++++++++
 lib/netfilter/nft_setelem_obj.c         |  215 +++++++++++++++++
 src/.gitignore                          |    3 +
 src/nf-set-add.c                        |  118 ++++++++++
 src/nf-set-delete.c                     |   95 ++++++++
 src/nf-set-dump.c                       |   96 ++++++++
 src/nf-table-dump.c                     |    2 +-
 17 files changed, 2271 insertions(+), 801 deletions(-)
 create mode 100644 include/netlink/netfilter/nft_set.h
 create mode 100644 include/netlink/netfilter/nft_setelem.h
 delete mode 100644 lib/netfilter/nft_hash_expr.c
 create mode 100644 lib/netfilter/nft_lookup_expr.c
 create mode 100644 lib/netfilter/nft_set.c
 delete mode 100644 lib/netfilter/nft_set_expr.c
 create mode 100644 lib/netfilter/nft_set_obj.c
 create mode 100644 lib/netfilter/nft_setelem.c
 create mode 100644 lib/netfilter/nft_setelem_obj.c
 create mode 100644 src/nf-set-add.c
 create mode 100644 src/nf-set-delete.c
 create mode 100644 src/nf-set-dump.c
Add support for the new set API to maintain sets independantly of the ruleset.

Signed-off-by: Patrick McHardy <kaber at trash.net>

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 0309b9d..1c23073 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -11,6 +11,17 @@ enum nft_registers {
 };
 #define NFT_REG_MAX	(__NFT_REG_MAX - 1)
 
+/**
+ * enum nft_verdicts - nf_tables internal verdicts
+ *
+ * @NFT_CONTINUE: continue evaluation of the current rule
+ * @NFT_BREAK: terminate evaluation of the current rule
+ * @NFT_JUMP: push the current chain on the jump stack and jump to a chain
+ * @NFT_GOTO: jump to a chain without pushing the current chain on the jump stack
+ * @NFT_RETURN: return to the topmost chain on the jump stack
+ *
+ * The nf_tables verdicts share their numeric space with the netfilter verdicts.
+ */
 enum nft_verdicts {
 	NFT_CONTINUE	= -1,
 	NFT_BREAK	= -2,
@@ -29,9 +40,20 @@ enum nf_tables_msg_types {
 	NFT_MSG_NEWRULE,
 	NFT_MSG_GETRULE,
 	NFT_MSG_DELRULE,
+	NFT_MSG_NEWSET,
+	NFT_MSG_GETSET,
+	NFT_MSG_DELSET,
+	NFT_MSG_NEWSETELEM,
+	NFT_MSG_GETSETELEM,
+	NFT_MSG_DELSETELEM,
 	NFT_MSG_MAX,
 };
 
+/**
+ * enum nft_list_attributes - nf_tables generic list netlink attributes
+ *
+ * @NFTA_LIST_ELEM: list element (NLA_NESTED)
+ */
 enum nft_list_attributes {
 	NFTA_LIST_UNPEC,
 	NFTA_LIST_ELEM,
@@ -39,6 +61,12 @@ enum nft_list_attributes {
 };
 #define NFTA_LIST_MAX		(__NFTA_LIST_MAX - 1)
 
+/**
+ * enum nft_hook_attributes - nf_tables netfilter hook netlink attributes
+ *
+ * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
+ * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
+ */
 enum nft_hook_attributes {
 	NFTA_HOOK_UNSPEC,
 	NFTA_HOOK_HOOKNUM,
@@ -47,6 +75,11 @@ enum nft_hook_attributes {
 };
 #define NFTA_HOOK_MAX		(__NFTA_HOOK_MAX - 1)
 
+/**
+ * enum nft_table_attributes - nf_tables table netlink attributes
+ *
+ * @NFTA_TABLE_NAME: name of the table (NLA_STRING)
+ */
 enum nft_table_attributes {
 	NFTA_TABLE_UNSPEC,
 	NFTA_TABLE_NAME,
@@ -54,6 +87,13 @@ enum nft_table_attributes {
 };
 #define NFTA_TABLE_MAX		(__NFTA_TABLE_MAX - 1)
 
+/**
+ * enum nft_chain_attributes - nf_tables chain netlink attributes
+ *
+ * @NFTA_CHAIN_TABLE: name of the table containing the chain (NLA_STRING)
+ * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING)
+ * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes)
+ */
 enum nft_chain_attributes {
 	NFTA_CHAIN_UNSPEC,
 	NFTA_CHAIN_TABLE,
@@ -63,6 +103,14 @@ enum nft_chain_attributes {
 };
 #define NFTA_CHAIN_MAX		(__NFTA_CHAIN_MAX - 1)
 
+/**
+ * enum nft_rule_attributes - nf_tables rule netlink attributes
+ *
+ * @NFTA_RULE_TABLE: name of the table containing the rule (NLA_STRING)
+ * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING)
+ * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U16)
+ * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes)
+ */
 enum nft_rule_attributes {
 	NFTA_RULE_UNSPEC,
 	NFTA_RULE_TABLE,
@@ -73,6 +121,113 @@ enum nft_rule_attributes {
 };
 #define NFTA_RULE_MAX		(__NFTA_RULE_MAX - 1)
 
+/**
+ * enum nft_set_flags - nf_tables set flags
+ *
+ * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink
+ * @NFT_SET_CONSTANT: set contents may not change while bound
+ * @NFT_SET_INTERVAL: set contains intervals
+ * @NFT_SET_MAP: set is used as a dictionary
+ */
+enum nft_set_flags {
+	NFT_SET_ANONYMOUS		= 0x1,
+	NFT_SET_CONSTANT		= 0x2,
+	NFT_SET_INTERVAL		= 0x4,
+	NFT_SET_MAP			= 0x8,
+};
+
+/**
+ * enum nft_set_attributes - nf_tables set netlink attributes
+ *
+ * @NFTA_SET_TABLE: table name (NLA_STRING)
+ * @NFTA_SET_NAME: set name (NLA_STRING)
+ * @NFTA_SET_FLAGS: bitmask of enum nft_set_flags (NLA_U32)
+ * @NFTA_SET_KEY_TYPE: key data type, informational purpose only (NLA_U32)
+ * @NFTA_SET_KEY_LEN: key data length (NLA_U32)
+ * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32)
+ * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
+ */
+enum nft_set_attributes {
+	NFTA_SET_UNSPEC,
+	NFTA_SET_TABLE,
+	NFTA_SET_NAME,
+	NFTA_SET_FLAGS,
+	NFTA_SET_KEY_TYPE,
+	NFTA_SET_KEY_LEN,
+	NFTA_SET_DATA_TYPE,
+	NFTA_SET_DATA_LEN,
+	__NFTA_SET_MAX
+};
+#define NFTA_SET_MAX		(__NFTA_SET_MAX - 1)
+
+/**
+ * enum nft_set_elem_flags - nf_tables set element flags
+ *
+ * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
+ */
+enum nft_set_elem_flags {
+	NFT_SET_ELEM_INTERVAL_END	= 0x1,
+};
+
+/**
+ * enum nft_set_elem_attributes - nf_tables set element netlink attributes
+ *
+ * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
+ * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
+ * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
+ */
+enum nft_set_elem_attributes {
+	NFTA_SET_ELEM_UNSPEC,
+	NFTA_SET_ELEM_KEY,
+	NFTA_SET_ELEM_DATA,
+	NFTA_SET_ELEM_FLAGS,
+	__NFTA_SET_ELEM_MAX
+};
+#define NFTA_SET_ELEM_MAX	(__NFTA_SET_ELEM_MAX - 1)
+
+/**
+ * enum nft_set_elem_list_attributes - nf_tables set element list netlink attributes
+ *
+ * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
+ */
+enum nft_set_elem_list_attributes {
+	NFTA_SET_ELEM_LIST_UNSPEC,
+	NFTA_SET_ELEM_LIST_TABLE,
+	NFTA_SET_ELEM_LIST_SET,
+	NFTA_SET_ELEM_LIST_ELEMENTS,
+	__NFTA_SET_ELEM_LIST_MAX
+};
+#define NFTA_SET_ELEM_LIST_MAX	(__NFTA_SET_ELEM_LIST_MAX - 1)
+
+/**
+ * enum nft_data_types - nf_tables data types
+ *
+ * @NFT_DATA_VALUE: generic data
+ * @NFT_DATA_VERDICT: netfilter verdict
+ *
+ * The type of data is usually determined by the kernel directly and is not
+ * explicitly specified by userspace. The only difference are sets, where
+ * userspace specifies the key and mapping data types.
+ *
+ * The values 0xffffff00-0xffffffff are reserved for internally used types.
+ * The remaining range can be freely used by userspace to encode types, all
+ * values are equivalent to NFT_DATA_VALUE.
+ */
+enum nft_data_types {
+	NFT_DATA_VALUE,
+	NFT_DATA_VERDICT	= 0xffffff00U,
+};
+
+#define NFT_DATA_RESERVED_MASK	0xffffff00U
+
+/**
+ * enum nft_data_attributes - nf_tables data netlink attributes
+ *
+ * @NFTA_DATA_VALUE: generic data (NLA_BINARY)
+ * @NFTA_DATA_VERDICT: nf_tables verdict (NLA_NESTED: nft_verdict_attributes)
+ */
 enum nft_data_attributes {
 	NFTA_DATA_UNSPEC,
 	NFTA_DATA_VALUE,
@@ -81,6 +236,12 @@ enum nft_data_attributes {
 };
 #define NFTA_DATA_MAX		(__NFTA_DATA_MAX - 1)
 
+/**
+ * enum nft_verdict_attributes - nf_tables verdict netlink attributes
+ *
+ * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
+ * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
+ */
 enum nft_verdict_attributes {
 	NFTA_VERDICT_UNSPEC,
 	NFTA_VERDICT_CODE,
@@ -89,6 +250,12 @@ enum nft_verdict_attributes {
 };
 #define NFTA_VERDICT_MAX	(__NFTA_VERDICT_MAX - 1)
 
+/**
+ * enum nft_expr_attributes - nf_tables expression netlink attributes
+ *
+ * @NFTA_EXPR_NAME: name of the expression type (NLA_STRING)
+ * @NFTA_EXPR_DATA: type specific data (NLA_NESTED)
+ */
 enum nft_expr_attributes {
 	NFTA_EXPR_UNSPEC,
 	NFTA_EXPR_NAME,
@@ -97,6 +264,12 @@ enum nft_expr_attributes {
 };
 #define NFTA_EXPR_MAX		(__NFTA_EXPR_MAX - 1)
 
+/**
+ * enum nft_immediate_attributes - nf_tables immediate expression netlink attributes
+ *
+ * @NFTA_IMMEDIATE_DREG: destination register to load data into (NLA_U32)
+ * @NFTA_IMMEDIATE_DATA: data to load (NLA_NESTED: nft_data_attributes)
+ */
 enum nft_immediate_attributes {
 	NFTA_IMMEDIATE_UNSPEC,
 	NFTA_IMMEDIATE_DREG,
@@ -105,6 +278,27 @@ enum nft_immediate_attributes {
 };
 #define NFTA_IMMEDIATE_MAX	(__NFTA_IMMEDIATE_MAX - 1)
 
+/**
+ * enum nft_bitwise_attributes - nf_tables bitwise expression netlink attributes
+ *
+ * @NFTA_BITWISE_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_LEN: length of operands (NLA_U32)
+ * @NFTA_BITWISE_MASK: mask value (NLA_NESTED: nft_data_attributes)
+ * @NFTA_BITWISE_XOR: xor value (NLA_NESTED: nft_data_attributes)
+ *
+ * The bitwise expression performs the following operation:
+ *
+ * dreg = (sreg & mask) ^ xor
+ *
+ * which allow to express all bitwise operations:
+ *
+ * 		mask	xor
+ * NOT:		1	1
+ * OR:		0	x
+ * XOR:		1	x
+ * AND:		x	0
+ */
 enum nft_bitwise_attributes {
 	NFTA_BITWISE_UNSPEC,
 	NFTA_BITWISE_SREG,
@@ -141,6 +335,13 @@ enum nft_cmp_ops {
 	NFT_CMP_GTE,
 };
 
+/**
+ * enum nft_cmp_attributes - nf_tables cmp expression netlink attributes
+ *
+ * @NFTA_CMP_SREG: source register of data to compare (NLA_U32: nft_registers)
+ * @NFTA_CMP_OP: cmp operation (NLA_U32: nft_cmp_ops)
+ * @NFTA_CMP_DATA: data to compare against (NLA_NESTED: nft_data_attributes)
+ */
 enum nft_cmp_attributes {
 	NFTA_CMP_UNSPEC,
 	NFTA_CMP_SREG,
@@ -150,65 +351,36 @@ enum nft_cmp_attributes {
 };
 #define NFTA_CMP_MAX		(__NFTA_CMP_MAX - 1)
 
-enum nft_set_elem_flags {
-	NFT_SE_INTERVAL_END	= 0x1,
-};
-
-enum nft_set_elem_attributes {
-	NFTA_SE_UNSPEC,
-	NFTA_SE_KEY,
-	NFTA_SE_DATA,
-	NFTA_SE_FLAGS,
-	__NFTA_SE_MAX
-};
-#define NFTA_SE_MAX		(__NFTA_SE_MAX - 1)
-
-enum nft_set_flags {
-	NFT_SET_INTERVAL	= 0x1,
-	NFT_SET_MAP		= 0x2,
-};
-
-enum nft_set_attributes {
-	NFTA_SET_UNSPEC,
-	NFTA_SET_FLAGS,
-	NFTA_SET_SREG,
-	NFTA_SET_DREG,
-	NFTA_SET_KLEN,
-	NFTA_SET_DLEN,
-	NFTA_SET_ELEMENTS,
-	__NFTA_SET_MAX
-};
-#define NFTA_SET_MAX		(__NFTA_SET_MAX - 1)
-
-enum nft_hash_flags {
-	NFT_HASH_MAP		= 0x1,
-};
-
-enum nft_hash_elem_attributes {
-	NFTA_HE_UNSPEC,
-	NFTA_HE_KEY,
-	NFTA_HE_DATA,
-	__NFTA_HE_MAX
-};
-#define NFTA_HE_MAX		(__NFTA_HE_MAX - 1)
-
-enum nft_hash_attributes {
-	NFTA_HASH_UNSPEC,
-	NFTA_HASH_FLAGS,
-	NFTA_HASH_SREG,
-	NFTA_HASH_DREG,
-	NFTA_HASH_KLEN,
-	NFTA_HASH_ELEMENTS,
-	__NFTA_HASH_MAX
-};
-#define NFTA_HASH_MAX		(__NFTA_HASH_MAX - 1)
-
+enum nft_lookup_attributes {
+	NFTA_LOOKUP_UNSPEC,
+	NFTA_LOOKUP_SET,
+	NFTA_LOOKUP_SREG,
+	NFTA_LOOKUP_DREG,
+	__NFTA_LOOKUP_MAX
+};
+#define NFTA_LOOKUP_MAX		(__NFTA_LOOKUP_MAX - 1)
+
+/**
+ * enum nft_payload_bases - nf_tables payload expression offset bases
+ *
+ * @NFT_PAYLOAD_LL_HEADER: link layer header
+ * @NFT_PAYLOAD_NETWORK_HEADER: network header
+ * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
+ */
 enum nft_payload_bases {
 	NFT_PAYLOAD_LL_HEADER,
 	NFT_PAYLOAD_NETWORK_HEADER,
 	NFT_PAYLOAD_TRANSPORT_HEADER,
 };
 
+/**
+ * enum nft_payload_attributes - nf_tables payload expression netlink attributes
+ *
+ * @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
+ * @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
+ * @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
+ * @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
+ */
 enum nft_payload_attributes {
 	NFTA_PAYLOAD_UNSPEC,
 	NFTA_PAYLOAD_DREG,
@@ -229,6 +401,25 @@ enum nft_exthdr_attributes {
 };
 #define NFTA_EXTHDR_MAX		(__NFTA_EXTHDR_MAX - 1)
 
+/**
+ * enum nft_meta_keys - nf_tables meta expression keys
+ *
+ * @NFT_META_LEN: packet length (skb->len)
+ * @NFT_META_PROTOCOL: packet ethertype protocol (skb->protocol), invalid in OUTPUT
+ * @NFT_META_PRIORITY: packet priority (skb->priority)
+ * @NFT_META_MARK: packet mark (skb->mark)
+ * @NFT_META_IIF: packet input interface index (dev->ifindex)
+ * @NFT_META_OIF: packet output interface index (dev->ifindex)
+ * @NFT_META_IIFNAME: packet input interface name (dev->name)
+ * @NFT_META_OIFNAME: packet output interface name (dev->name)
+ * @NFT_META_IIFTYPE: packet input interface type (dev->type)
+ * @NFT_META_OIFTYPE: packet output interface type (dev->type)
+ * @NFT_META_SKUID: originating socket UID (fsuid)
+ * @NFT_META_SKGID: originating socket GID (fsgid)
+ * @NFT_META_NFTRACE: packet nftrace bit
+ * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid)
+ * @NFT_META_SECMARK: packet secmark (skb->secmark)
+ */
 enum nft_meta_keys {
 	NFT_META_LEN,
 	NFT_META_PROTOCOL,
@@ -247,6 +438,12 @@ enum nft_meta_keys {
 	NFT_META_SECMARK,
 };
 
+/**
+ * enum nft_meta_attributes - nf_tables meta expression netlink attributes
+ *
+ * @NFTA_META_DREG: destination register (NLA_U32)
+ * @NFTA_META_KEY: meta data item to load (NLA_U32: nft_meta_keys)
+ */
 enum nft_meta_attributes {
 	NFTA_META_UNSPEC,
 	NFTA_META_DREG,
@@ -255,6 +452,23 @@ enum nft_meta_attributes {
 };
 #define NFTA_META_MAX		(__NFTA_META_MAX - 1)
 
+/**
+ * enum nft_ct_keys - nf_tables ct expression keys
+ *
+ * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info)
+ * @NFT_CT_DIRECTION: conntrack direction (enum ip_conntrack_dir)
+ * @NFT_CT_STATUS: conntrack status (bitmask of enum ip_conntrack_status)
+ * @NFT_CT_MARK: conntrack mark value
+ * @NFT_CT_SECMARK: conntrack secmark value
+ * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms
+ * @NFT_CT_HELPER: connection tracking helper assigned to conntrack
+ * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol
+ * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address)
+ * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address)
+ * @NFT_CT_PROTOCOL: conntrack layer 4 protocol
+ * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source
+ * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination
+ */
 enum nft_ct_keys {
 	NFT_CT_STATE,
 	NFT_CT_DIRECTION,
@@ -271,6 +485,13 @@ enum nft_ct_keys {
 	NFT_CT_PROTO_DST,
 };
 
+/**
+ * enum nft_ct_attributes - nf_tables ct expression netlink attributes
+ *
+ * @NFTA_CT_DREG: destination register (NLA_U32)
+ * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys)
+ * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8)
+ */
 enum nft_ct_attributes {
 	NFTA_CT_UNSPEC,
 	NFTA_CT_DREG,
@@ -280,6 +501,12 @@ enum nft_ct_attributes {
 };
 #define NFTA_CT_MAX		(__NFTA_CT_MAX - 1)
 
+/**
+ * enum nft_limit_attributes - nf_tables limit expression netlink attributes
+ *
+ * @NFTA_LIMIT_RATE: refill rate (NLA_U64)
+ * @NFTA_LIMIT_DEPTH: bucket depth (NLA_U64)
+ */
 enum nft_limit_attributes {
 	NFTA_LIMIT_UNSPEC,
 	NFTA_LIMIT_RATE,
@@ -288,6 +515,12 @@ enum nft_limit_attributes {
 };
 #define NFTA_LIMIT_MAX		(__NFTA_LIMIT_MAX - 1)
 
+/**
+ * enum nft_counter_attributes - nf_tables counter expression netlink attributes
+ *
+ * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
+ * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ */
 enum nft_counter_attributes {
 	NFTA_COUNTER_UNSPEC,
 	NFTA_COUNTER_BYTES,
@@ -296,6 +529,14 @@ enum nft_counter_attributes {
 };
 #define NFTA_COUNTER_MAX	(__NFTA_COUNTER_MAX - 1)
 
+/**
+ * enum nft_log_attributes - nf_tables log expression netlink attributes
+ *
+ * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
+ * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING)
+ * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32)
+ * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32)
+ */
 enum nft_log_attributes {
 	NFTA_LOG_UNSPEC,
 	NFTA_LOG_GROUP,
@@ -306,11 +547,23 @@ enum nft_log_attributes {
 };
 #define NFTA_LOG_MAX		(__NFTA_LOG_MAX - 1)
 
+/**
+ * enum nft_reject_types - nf_tables reject expression reject types
+ *
+ * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
+ * @NFT_REJECT_TCP_RST: reject using TCP RST
+ */
 enum nft_reject_types {
 	NFT_REJECT_ICMP_UNREACH,
 	NFT_REJECT_TCP_RST,
 };
 
+/**
+ * enum nft_reject_attributes - nf_tables reject expression netlink attributes
+ *
+ * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types)
+ * @NFTA_REJECT_ICMP_CODE: ICMP code to use (NLA_U8)
+ */
 enum nft_reject_attributes {
 	NFTA_REJECT_UNSPEC,
 	NFTA_REJECT_TYPE,
@@ -319,11 +572,26 @@ enum nft_reject_attributes {
 };
 #define NFTA_REJECT_MAX		(__NFTA_REJECT_MAX - 1)
 
+/**
+ * enum nft_nat_types - nf_tables nat expression NAT types
+ *
+ * @NFT_NAT_SNAT: source NAT
+ * @NFT_NAT_DNAT: destination NAT
+ */
 enum nft_nat_types {
 	NFT_NAT_SNAT,
 	NFT_NAT_DNAT,
 };
 
+/**
+ * enum nft_nat_attributes - nf_tables nat expression netlink attributes
+ *
+ * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types)
+ * @NFTA_NAT_ADDR_MIN: source register of address range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
+ * @NFTA_NAT_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
+ */
 enum nft_nat_attributes {
 	NFTA_NAT_UNSPEC,
 	NFTA_NAT_TYPE,
diff --git a/include/netlink-types.h b/include/netlink-types.h
index e8b637c..7ae0c3d 100644
--- a/include/netlink-types.h
+++ b/include/netlink-types.h
@@ -835,6 +835,28 @@ struct nfnl_nft_expr {
 	struct nl_list_head	expr_list;
 };
 
+struct nfnl_nft_set {
+	NLHDR_COMMON
+
+	uint8_t			set_family;
+	struct nl_data		*set_table;
+	struct nl_data		*set_name;
+	uint32_t		set_keytype;
+	uint8_t			set_keylen;
+	uint32_t		set_datatype;
+	uint8_t			set_datalen;
+	uint32_t		set_flags;
+};
+
+struct nfnl_nft_setelem {
+	NLHDR_COMMON
+
+	struct nl_list_head	elem_list;
+	struct nfnl_nft_data	*elem_key;
+	struct nfnl_nft_data	*elem_data;
+	uint32_t		elem_flags;
+};
+
 struct nfnl_nft_data {
 	uint32_t		d_type;
 	size_t			d_size;
diff --git a/include/netlink/netfilter/nft_expr.h b/include/netlink/netfilter/nft_expr.h
index f6e2b53..b7895f0 100644
--- a/include/netlink/netfilter/nft_expr.h
+++ b/include/netlink/netfilter/nft_expr.h
@@ -84,74 +84,16 @@ extern struct nfnl_nft_data *	nfnl_nft_cmp_get_data(const struct nfnl_nft_expr *
 extern char *			nfnl_nft_cmp_op2str(enum nft_cmp_ops, char *, size_t);
 extern enum nft_cmp_ops		nfnl_nft_cmp_str2op(const char *);
 
-extern int			nfnl_nft_set_init(struct nfnl_nft_expr *);
-extern void			nfnl_nft_set_set_flags(struct nfnl_nft_expr *,
-						       uint32_t);
-extern uint32_t			nfnl_nft_set_get_flags(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_set_set_sreg(struct nfnl_nft_expr *,
-						      enum nft_registers);
-extern enum nft_registers	nfnl_nft_set_get_sreg(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_set_set_dreg(struct nfnl_nft_expr *,
-						      enum nft_registers);
-extern enum nft_registers	nfnl_nft_set_get_dreg(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_set_set_klen(struct nfnl_nft_expr *,
-						      unsigned int);
-extern unsigned int		nfnl_nft_set_get_klen(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_set_set_dlen(struct nfnl_nft_expr *,
-						      unsigned int);
-extern unsigned int		nfnl_nft_set_get_dlen(const struct nfnl_nft_expr *);
-
-extern int			nfnl_nft_set_add_elem(struct nfnl_nft_expr *,
-						      struct nfnl_nft_data *,
-						      enum nft_set_elem_flags);
-extern int			nfnl_nft_set_add_mapping(struct nfnl_nft_expr *,
-							 struct nfnl_nft_data *,
-							 struct nfnl_nft_data *,
-							 enum nft_set_elem_flags);
-extern void			nfnl_nft_set_foreach_elem(const struct nfnl_nft_expr *,
-							  void (*)(struct nfnl_nft_data *,
-								   enum nft_set_elem_flags,
-							  	   void *),
-							  void *);
-extern void			nfnl_nft_set_foreach_mapping(const struct nfnl_nft_expr *,
-							     void (*)(struct nfnl_nft_data *,
-								      struct nfnl_nft_data *,
-								      enum nft_set_elem_flags,
-								      void *),
-							     void *);
-extern char *			nfnl_nft_set_flags2str(enum nft_set_flags,
-						       char *, size_t);
-extern enum nft_set_flags	nfnl_nft_set_str2flags(const char *);
-
-
-extern int			nfnl_nft_hash_init(struct nfnl_nft_expr *);
-extern void			nfnl_nft_hash_set_flags(struct nfnl_nft_expr *,
-							uint32_t);
-extern uint32_t			nfnl_nft_hash_get_flags(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_hash_set_sreg(struct nfnl_nft_expr *,
-						       enum nft_registers);
-extern enum nft_registers	nfnl_nft_hash_get_sreg(const struct nfnl_nft_expr *);
-extern void			nfnl_nft_hash_set_dreg(struct nfnl_nft_expr *,
-						       enum nft_registers);
-extern enum nft_registers	nfnl_nft_hash_get_dreg(const struct nfnl_nft_expr *);;
-extern void			nfnl_nft_hash_set_klen(struct nfnl_nft_expr *,
-						       unsigned int);
-extern unsigned int		nfnl_nft_hash_get_klen(const struct nfnl_nft_expr *);
-extern int			nfnl_nft_hash_add_mapping(struct nfnl_nft_expr *,
-							  struct nfnl_nft_data *,
-							  struct nfnl_nft_data *);
-extern int			nfnl_nft_hash_add_elem(struct nfnl_nft_expr *,
-						       struct nfnl_nft_data *);
-extern void			nfnl_nft_hash_foreach_elem(const struct nfnl_nft_expr *,
-							   void (*)(struct nfnl_nft_data *,
-							  	    void *),
-							   void *);
-
-extern void			nfnl_nft_hash_foreach_mapping(const struct nfnl_nft_expr *expr,
-							      void (*)(struct nfnl_nft_data *,
-								       struct nfnl_nft_data *,
-								       void *),
-							      void *);
+extern int			nfnl_nft_lookup_init(struct nfnl_nft_expr *);
+extern void			nfnl_nft_lookup_set_set(struct nfnl_nft_expr *,
+							const char *);
+extern const char *		nfnl_nft_lookup_get_set(const struct nfnl_nft_expr *);
+extern void			nfnl_nft_lookup_set_sreg(struct nfnl_nft_expr *,
+							 enum nft_registers);
+extern enum nft_registers	nfnl_nft_lookup_get_sreg(const struct nfnl_nft_expr *);
+extern void			nfnl_nft_lookup_set_dreg(struct nfnl_nft_expr *,
+							 enum nft_registers);
+extern enum nft_registers	nfnl_nft_lookup_get_dreg(const struct nfnl_nft_expr *);
 
 extern int			nfnl_nft_meta_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_meta_set_dreg(struct nfnl_nft_expr *,
diff --git a/include/netlink/netfilter/nft_set.h b/include/netlink/netfilter/nft_set.h
new file mode 100644
index 0000000..dd4c03f
--- /dev/null
+++ b/include/netlink/netfilter/nft_set.h
@@ -0,0 +1,90 @@
+#ifndef NETLINK_NFT_SET_H_
+#define NETLINK_NFT_SET_H_
+
+#include <netlink/netlink.h>
+#include <netlink/addr.h>
+#include <netlink/cache.h>
+#include <netlink/msg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nfnl_nft_set;
+enum nft_data_types;
+
+extern struct nl_object_ops nft_set_obj_ops;
+
+/* General */
+extern struct nfnl_nft_set *	nfnl_nft_set_alloc(void);
+extern void		nfnl_nft_set_get(struct nfnl_nft_set *);
+extern void		nfnl_nft_set_put(struct nfnl_nft_set *);
+
+extern int		nfnl_nft_set_alloc_cache(struct nl_sock *, int, const char *,
+						 struct nl_cache **);
+
+extern int		nfnlmsg_nft_set_group(struct nlmsghdr *);
+extern int		nfnlmsg_nft_set_parse(struct nlmsghdr *,
+					      struct nfnl_nft_set **);
+
+extern int		nfnl_nft_set_dump_request(struct nl_sock *, int, const char *);
+
+extern int		nfnl_nft_set_build_add_request(struct nfnl_nft_set *,
+						       int, struct nl_msg **);
+extern int		nfnl_nft_set_add(struct nl_sock *,
+					 struct nfnl_nft_set *, int);
+
+extern int		nfnl_nft_set_build_delete_request(struct nfnl_nft_set *,
+							  int, struct nl_msg **);
+extern int		nfnl_nft_set_delete(struct nl_sock *,
+					    struct nfnl_nft_set *, int);
+
+extern int		nfnl_nft_set_build_query_request(struct nfnl_nft_set *,
+							 int, struct nl_msg **);
+extern int		nfnl_nft_set_query(struct nl_sock *,
+					   struct nfnl_nft_set *, int);
+
+extern void		nfnl_nft_set_set_family(struct nfnl_nft_set *, uint8_t);
+extern int		nfnl_nft_set_test_family(const struct nfnl_nft_set *);
+extern uint8_t		nfnl_nft_set_get_family(const struct nfnl_nft_set *);
+
+extern int		nfnl_nft_set_set_table(struct nfnl_nft_set *,
+					       const char *, int);
+extern int		nfnl_nft_set_test_table(const struct nfnl_nft_set *);
+extern const char *	nfnl_nft_set_get_table(const struct nfnl_nft_set *);
+
+extern int		nfnl_nft_set_set_name(struct nfnl_nft_set *,
+					      const char *, int);
+extern int		nfnl_nft_set_test_name(const struct nfnl_nft_set *);
+extern const char *	nfnl_nft_set_get_name(const struct nfnl_nft_set *);
+
+extern void		nfnl_nft_set_set_flags(struct nfnl_nft_set *, uint32_t);
+extern int		nfnl_nft_set_test_flags(const struct nfnl_nft_set *);
+extern uint32_t		nfnl_nft_set_get_flags(const struct nfnl_nft_set *);
+
+extern void		nfnl_nft_set_set_keytype(struct nfnl_nft_set *,
+						 enum nft_data_types);
+extern int		nfnl_nft_set_test_keytype(const struct nfnl_nft_set *);
+extern enum nft_data_types nfnl_nft_set_get_keytype(const struct nfnl_nft_set *);
+
+extern void		nfnl_nft_set_set_keylen(struct nfnl_nft_set *, uint32_t);
+extern int		nfnl_nft_set_test_keylen(const struct nfnl_nft_set *);
+extern uint32_t		nfnl_nft_set_get_keylen(const struct nfnl_nft_set *);
+
+extern void		nfnl_nft_set_set_datatype(struct nfnl_nft_set *,
+						   enum nft_data_types);
+extern int		nfnl_nft_set_test_datatype(const struct nfnl_nft_set *);
+extern enum nft_data_types nfnl_nft_set_get_datatype(const struct nfnl_nft_set *);
+
+extern void		nfnl_nft_set_set_datalen(struct nfnl_nft_set *, uint32_t);
+extern int		nfnl_nft_set_test_datalen(const struct nfnl_nft_set *);
+extern uint32_t		nfnl_nft_set_get_datalen(const struct nfnl_nft_set *);
+
+extern char *		nfnl_nft_set_flags2str(uint32_t, char *, size_t);
+extern uint32_t		nfnl_nft_set_str2flags(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/netfilter/nft_setelem.h b/include/netlink/netfilter/nft_setelem.h
new file mode 100644
index 0000000..3e94b2c
--- /dev/null
+++ b/include/netlink/netfilter/nft_setelem.h
@@ -0,0 +1,63 @@
+#ifndef NETLINK_NFT_SETELEM_H_
+#define NETLINK_NFT_SETELEM_H_
+
+#include <netlink/netlink.h>
+#include <netlink/addr.h>
+#include <netlink/cache.h>
+#include <netlink/msg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nfnl_nft_setelem;
+struct nfnl_nft_set;
+
+extern struct nl_object_ops nft_setelem_obj_ops;
+
+/* General */
+extern struct nfnl_nft_setelem *nfnl_nft_setelem_alloc(void);
+extern void			nfnl_nft_setelem_get(struct nfnl_nft_setelem *);
+extern void			nfnl_nft_setelem_put(struct nfnl_nft_setelem *);
+
+extern int			nfnl_nft_setelem_alloc_cache(struct nl_sock *,
+							     struct nfnl_nft_set *,
+							     struct nl_cache **);
+
+extern int			nfnl_nft_setelem_dump_request(struct nl_sock *,
+							      struct nfnl_nft_set *);
+
+extern int			nfnl_nft_setelem_add(struct nl_sock *,
+						     struct nfnl_nft_set *,
+						     struct nl_cache *, int);
+
+extern int			nfnl_nft_setelem_delete(struct nl_sock *,
+							struct nfnl_nft_set *,
+							struct nl_cache *, int);
+
+extern int			nfnl_nft_setelem_query(struct nl_sock *,
+						       struct nfnl_nft_set *, int);
+
+extern void			nfnl_nft_setelem_set_key(struct nfnl_nft_setelem *,
+							 struct nfnl_nft_data *);
+extern int			nfnl_nft_setelem_test_key(const struct nfnl_nft_setelem *);
+extern struct nfnl_nft_data *	nfnl_nft_setelem_get_key(const struct nfnl_nft_setelem *);
+
+extern void			nfnl_nft_setelem_set_data(struct nfnl_nft_setelem *,
+							  struct nfnl_nft_data *);
+extern int			nfnl_nft_setelem_test_data(const struct nfnl_nft_setelem *);
+extern struct nfnl_nft_data *	nfnl_nft_setelem_get_data(const struct nfnl_nft_setelem *);
+
+extern void			nfnl_nft_setelem_set_flags(struct nfnl_nft_setelem *,
+							   uint32_t);
+extern int			nfnl_nft_setelem_test_flags(const struct nfnl_nft_setelem *);
+extern uint32_t			nfnl_nft_setelem_get_flags(const struct nfnl_nft_setelem *);
+
+extern char *			nfnl_nft_setelem_flags2str(uint32_t, char *, size_t);
+extern uint32_t			nfnl_nft_setelem_str2flags(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/netfilter/nft_hash_expr.c b/lib/netfilter/nft_hash_expr.c
deleted file mode 100644
index 637f00e..0000000
--- a/lib/netfilter/nft_hash_expr.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * lib/netfilter/nft_hash_expr.c
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- */
-
-#include <sys/types.h>
-#include <linux/netfilter/nf_tables.h>
-
-#include <netlink-local.h>
-#include <netlink/attr.h>
-#include <netlink/netfilter/nfnl.h>
-#include <netlink/netfilter/nft_data.h>
-#include <netlink/netfilter/nft_rule.h>
-#include <netlink/netfilter/nft_expr.h>
-#include <netlink/netfilter/nft-expr-modules.h>
-
-struct nft_hash_expr {
-	struct nl_list_head	hash_elements;
-	enum nft_registers	sreg;
-	enum nft_registers	dreg;
-	uint32_t		flags;
-	uint32_t		klen;
-};
-
-struct nft_hash_elem {
-	struct nl_list_head	list;
-	struct nfnl_nft_data *	key;
-	struct nfnl_nft_data *	data;
-};
-
-static struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
-	[NFTA_HASH_FLAGS]		= { .type = NLA_U32 },
-	[NFTA_HASH_SREG]		= { .type = NLA_U32 },
-	[NFTA_HASH_DREG]		= { .type = NLA_U32 },
-	[NFTA_HASH_KLEN]		= { .type = NLA_U32 },
-	[NFTA_HASH_ELEMENTS]		= { .type = NLA_NESTED },
-};
-
-static struct nla_policy nft_he_policy[NFTA_HE_MAX + 1] = {
-	[NFTA_HE_KEY]			= { .type = NLA_NESTED },
-	[NFTA_HE_DATA]			= { .type = NLA_NESTED },
-};
-
-static struct nft_expr_ops hash_expr_ops;
-
-static struct nft_hash_expr *nft_hash(const struct nfnl_nft_expr *expr)
-{
-	return (struct nft_hash_expr *) expr->expr_data;
-}
-
-static struct nft_hash_expr *nft_hash_expr_alloc(struct nfnl_nft_expr *expr)
-{
-	struct nft_hash_expr *hash;
-
-	if (!expr->expr_data) {
-		hash = calloc(1, sizeof(struct nft_hash_expr));
-		if (hash) {
-			nl_init_list_head(&hash->hash_elements);
-			expr->expr_data = hash;
-			expr->expr_ops = &hash_expr_ops;
-		}
-	}
-
-	return nft_hash(expr);
-}
-
-static void nft_hash_free_data(struct nfnl_nft_expr *expr)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem, *next;
-
-	nl_list_for_each_entry_safe(elem, next, &hash->hash_elements, list) {
-		nfnl_nft_data_free(elem->key);
-		if (elem->data != NULL)
-			nfnl_nft_data_free(elem->data);
-		free(elem);
-	}
-}
-
-static int nft_hash_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
-{
-	struct nft_hash_expr *hash;
-	struct nft_hash_elem *elem;
-	struct nlattr *nla;
-	struct nlattr *he[NFTA_HE_MAX + 1];
-	int rem;
-
-	hash = nft_hash_expr_alloc(expr);
-	if (hash == NULL)
-		return -ENOMEM;
-
-	if (tb[NFTA_HASH_FLAGS] != NULL)
-		hash->flags = ntohl(nla_get_u32(tb[NFTA_HASH_FLAGS]));
-	if (tb[NFTA_HASH_SREG] != NULL)
-		hash->sreg = ntohl(nla_get_u32(tb[NFTA_HASH_SREG]));
-	if (tb[NFTA_HASH_DREG] != NULL)
-		hash->dreg = ntohl(nla_get_u32(tb[NFTA_HASH_DREG]));
-	if (tb[NFTA_HASH_KLEN] != NULL)
-		hash->klen = ntohl(nla_get_u32(tb[NFTA_HASH_KLEN]));
-
-	if (tb[NFTA_HASH_ELEMENTS] != NULL) {
-		nla_for_each_nested(nla, tb[NFTA_HASH_ELEMENTS], rem) {
-			if (nla_type(nla) != NFTA_LIST_ELEM)
-				goto errout;
-			if (nla_parse_nested(he, NFTA_HE_MAX, nla, nft_he_policy) < 0)
-				goto errout;
-
-			elem = malloc(sizeof(*elem));
-			if (elem == NULL)
-				goto errout;
-			elem->data = NULL;
-
-			if (he[NFTA_HE_KEY] == NULL)
-				goto errout;
-			if (nfnl_nft_data_parse(he[NFTA_HE_KEY], &elem->key) < 0)
-				goto errout;
-			if (he[NFTA_HE_DATA] != NULL) {
-				if (nfnl_nft_data_parse(he[NFTA_HE_DATA], &elem->data) < 0)
-					goto errout;
-			}
-			nl_list_add_tail(&elem->list, &hash->hash_elements);
-		}
-	}
-
-	return 0;
-
-errout:
-	return -1;
-}
-
-static int nft_hash_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem;
-	struct nlattr *list, *nest;
-
-	if (nla_put_u32(msg, NFTA_HASH_FLAGS, htonl(hash->flags)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_HASH_SREG, htonl(hash->sreg)) < 0)
-		goto errout;
-	if (hash->flags & NFT_HASH_MAP &&
-	    nla_put_u32(msg, NFTA_HASH_DREG, htonl(hash->dreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_HASH_KLEN, htonl(hash->klen)) < 0)
-		goto errout;
-
-	list = nla_nest_start(msg, NFTA_HASH_ELEMENTS);
-	if (list == NULL)
-		goto errout;
-	nl_list_for_each_entry(elem, &hash->hash_elements, list) {
-		nest = nla_nest_start(msg, NFTA_LIST_ELEM);
-		if (nest == NULL)
-			goto errout;
-		if (nfnl_nft_data_put(msg, NFTA_HE_KEY, elem->key) < 0)
-			goto errout;
-		if (hash->flags & NFT_HASH_MAP &&
-		    nfnl_nft_data_put(msg, NFTA_HE_DATA, elem->data) < 0)
-			goto errout;
-		nla_nest_end(msg, nest);
-	}
-	nla_nest_end(msg, list);
-	return 0;
-
-errout:
-	return -1;
-}
-
-static void nft_hash_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem;
-	char *delim = "";
-
-	if (hash->flags & NFT_HASH_MAP)
-		nl_dump(p, "map reg %u load result reg %u\n",
-			hash->sreg, hash->dreg);
-	else
-		nl_dump(p, "lookup reg %u\n", hash->sreg);
-
-	delim = "    { ";
-	nl_list_for_each_entry(elem, &hash->hash_elements, list) {
-		nl_dump(p, "%s", delim);
-		nfnl_nft_data_dump(p, elem->key);
-		if (elem->data) {
-			nl_dump(p, " : ");
-			nfnl_nft_data_dump(p, elem->data);
-		}
-		delim = ",\n      ";
-	}
-	nl_dump(p, " }");
-}
-
-int nfnl_nft_hash_init(struct nfnl_nft_expr *expr)
-{
-	if (nft_hash_expr_alloc(expr) == NULL)
-		return -1;
-	return 0;
-}
-
-void nfnl_nft_hash_set_flags(struct nfnl_nft_expr *expr, uint32_t flags)
-{
-	nft_hash(expr)->flags = flags;
-}
-
-uint32_t nfnl_nft_hash_get_flags(const struct nfnl_nft_expr *expr)
-{
-	return nft_hash(expr)->flags;
-}
-
-void nfnl_nft_hash_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
-{
-	nft_hash(expr)->sreg = reg;
-}
-
-enum nft_registers nfnl_nft_hash_get_sreg(const struct nfnl_nft_expr *expr)
-{
-	return nft_hash(expr)->sreg;
-}
-
-void nfnl_nft_hash_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
-{
-	nft_hash(expr)->dreg = reg;
-}
-
-enum nft_registers nfnl_nft_hash_get_dreg(const struct nfnl_nft_expr *expr)
-{
-	return nft_hash(expr)->dreg;
-}
-
-int nfnl_nft_hash_add_mapping(struct nfnl_nft_expr *expr,
-			      struct nfnl_nft_data *key,
-			      struct nfnl_nft_data *data)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem;
-
-	if (data != NULL && !(hash->flags & NFT_SET_MAP))
-		return -1;
-
-	elem = malloc(sizeof(*elem));
-	if (elem == NULL)
-		return -1;
-
-	elem->key  = key;
-	elem->data = data;
-	nl_list_add_tail(&elem->list, &hash->hash_elements);
-	return 0;
-}
-
-int nfnl_nft_hash_add_elem(struct nfnl_nft_expr *expr, struct nfnl_nft_data *data)
-{
-	return nfnl_nft_hash_add_mapping(expr, data, NULL);
-}
-
-void nfnl_nft_hash_foreach_elem(const struct nfnl_nft_expr *expr,
-				void (*cb)(struct nfnl_nft_data *, void *),
-				void *arg)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem;
-
-	nl_list_for_each_entry(elem, &hash->hash_elements, list)
-		cb(elem->key, arg);
-}
-
-void nfnl_nft_hash_foreach_mapping(const struct nfnl_nft_expr *expr,
-				   void (*cb)(struct nfnl_nft_data *,
-				  	      struct nfnl_nft_data *, void *),
-				   void *arg)
-{
-	struct nft_hash_expr *hash = nft_hash(expr);
-	struct nft_hash_elem *elem;
-
-	nl_list_for_each_entry(elem, &hash->hash_elements, list)
-		cb(elem->key, elem->data, arg);
-}
-
-static struct nft_expr_ops hash_expr_ops = {
-	.eo_name		= "hash",
-	.eo_dump[NL_DUMP_DETAILS]	= nft_hash_dump,
-	.eo_get_opts		= nft_hash_get_opts,
-	.eo_msg_parser		= nft_hash_msg_parser,
-	.eo_free_data		= nft_hash_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_hash_policy,
-	.eo_maxattr		= NFTA_HASH_MAX,
-};
-
-static void __init nft_hash_expr_init(void)
-{
-	nft_expr_register(&hash_expr_ops);
-}
-
-static void __exit nft_hash_expr_exit(void)
-{
-	nft_expr_unregister(&hash_expr_ops);
-}
-
-/** @} */
diff --git a/lib/netfilter/nft_lookup_expr.c b/lib/netfilter/nft_lookup_expr.c
new file mode 100644
index 0000000..45931dd
--- /dev/null
+++ b/lib/netfilter/nft_lookup_expr.c
@@ -0,0 +1,179 @@
+/*
+ * lib/netfilter/nft_lookup_expr.c
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/nft_data.h>
+#include <netlink/netfilter/nft_rule.h>
+#include <netlink/netfilter/nft_expr.h>
+#include <netlink/netfilter/nft-expr-modules.h>
+
+struct nft_lookup_expr {
+	char			*set;
+	enum nft_registers	sreg;
+	enum nft_registers	dreg;
+};
+
+static struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
+	[NFTA_LOOKUP_SET]		= { .type = NLA_STRING },
+	[NFTA_LOOKUP_SREG]		= { .type = NLA_U32 },
+	[NFTA_LOOKUP_DREG]		= { .type = NLA_U32 },
+};
+
+static struct nft_expr_ops lookup_expr_ops;
+
+static struct nft_lookup_expr *nft_lookup(const struct nfnl_nft_expr *expr)
+{
+	return (struct nft_lookup_expr *) expr->expr_data;
+}
+
+static struct nft_lookup_expr *nft_lookup_expr_alloc(struct nfnl_nft_expr *expr)
+{
+	struct nft_lookup_expr *lookup;
+
+	if (!expr->expr_data) {
+		lookup = calloc(1, sizeof(struct nft_lookup_expr));
+		if (lookup) {
+			memset(lookup, 0, sizeof(*lookup));
+			expr->expr_data = lookup;
+			expr->expr_ops = &lookup_expr_ops;
+		}
+	}
+
+	return nft_lookup(expr);
+}
+
+static void nft_lookup_free_data(struct nfnl_nft_expr *expr)
+{
+	struct nft_lookup_expr *lookup = nft_lookup(expr);
+
+	free(lookup->set);
+}
+
+static int nft_lookup_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
+{
+	struct nft_lookup_expr *lookup;
+
+	lookup = nft_lookup_expr_alloc(expr);
+	if (lookup == NULL)
+		goto err1;
+
+	if (tb[NFTA_LOOKUP_SET] != NULL) {
+		lookup->set = nla_strdup(tb[NFTA_LOOKUP_SET]);
+		if (lookup->set == NULL)
+			goto err2;
+	}
+	if (tb[NFTA_LOOKUP_SREG] != NULL)
+		lookup->sreg = ntohl(nla_get_u32(tb[NFTA_LOOKUP_SREG]));
+	if (tb[NFTA_LOOKUP_DREG] != NULL)
+		lookup->dreg = ntohl(nla_get_u32(tb[NFTA_LOOKUP_DREG]));
+
+	return 0;
+
+err2:
+	free(lookup);
+err1:
+	return ENOMEM;
+}
+
+static int nft_lookup_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
+{
+	struct nft_lookup_expr *lookup = nft_lookup(expr);
+
+	if (nla_put_u32(msg, NFTA_LOOKUP_SREG, htonl(lookup->sreg)) < 0)
+		goto nla_put_failure;
+	if (lookup->set != NULL)
+		NLA_PUT_STRING(msg, NFTA_LOOKUP_SET, lookup->set);
+
+#if 0
+	if (lookup->flags & NFT_LOOKUP_MAP) {
+		if (nla_put_u32(msg, NFTA_LOOKUP_DREG, htonl(lookup->dreg)) < 0)
+			goto errout;
+	}
+#endif
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+static void nft_lookup_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
+{
+	struct nft_lookup_expr *lookup = nft_lookup(expr);
+
+	nl_dump(p, "reg %u set %s", lookup->sreg, lookup->set);
+}
+
+int nfnl_nft_lookup_init(struct nfnl_nft_expr *expr)
+{
+	if (nft_lookup_expr_alloc(expr) == NULL)
+		return -1;
+	return 0;
+}
+
+void nfnl_nft_lookup_set_set(struct nfnl_nft_expr *expr, const char *set)
+{
+	nft_lookup(expr)->set = strdup(set);
+}
+
+const char *nfnl_nft_lookup_get_set(const struct nfnl_nft_expr *expr)
+{
+	return nft_lookup(expr)->set;
+}
+
+void nfnl_nft_lookup_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
+{
+	nft_lookup(expr)->sreg = reg;
+}
+
+enum nft_registers nfnl_nft_lookup_get_sreg(const struct nfnl_nft_expr *expr)
+{
+	return nft_lookup(expr)->sreg;
+}
+
+void nfnl_nft_lookup_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
+{
+	nft_lookup(expr)->dreg = reg;
+}
+
+enum nft_registers nfnl_nft_lookup_get_dreg(const struct nfnl_nft_expr *expr)
+{
+	return nft_lookup(expr)->dreg;
+}
+
+static struct nft_expr_ops lookup_expr_ops = {
+	.eo_name			= "lookup",
+	.eo_dump[NL_DUMP_DETAILS]	= nft_lookup_dump,
+	.eo_get_opts			= nft_lookup_get_opts,
+	.eo_msg_parser			= nft_lookup_msg_parser,
+	.eo_free_data			= nft_lookup_free_data,
+	.eo_clone			= NULL,
+	.eo_policy			= nft_lookup_policy,
+	.eo_maxattr			= NFTA_LOOKUP_MAX,
+};
+
+static void __init nft_lookup_expr_init(void)
+{
+	nft_expr_register(&lookup_expr_ops);
+}
+
+static void __exit nft_lookup_expr_exit(void)
+{
+	nft_expr_unregister(&lookup_expr_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/nft_set.c b/lib/netfilter/nft_set.c
new file mode 100644
index 0000000..b3191b1
--- /dev/null
+++ b/lib/netfilter/nft_set.c
@@ -0,0 +1,321 @@
+/*
+ * lib/netfilter/nft_set.c	nf_tables sets
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup nft nf_tables
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/nft_set.h>
+
+static struct nl_cache_ops nfnl_nft_set_ops;
+
+static struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
+	[NFTA_SET_NAME]			= { .type = NLA_STRING },
+	[NFTA_SET_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_SET_KEY_TYPE]		= { .type = NLA_U32 },
+	[NFTA_SET_KEY_LEN]		= { .type = NLA_U32 },
+	[NFTA_SET_DATA_TYPE]		= { .type = NLA_U32 },
+	[NFTA_SET_DATA_LEN]		= { .type = NLA_U32 },
+};
+
+int nfnlmsg_nft_set_parse(struct nlmsghdr *nlh, struct nfnl_nft_set **result)
+{
+	struct nfnl_nft_set *set;
+	struct nlattr *tb[NFTA_SET_MAX + 1], *nla;
+	int err;
+
+	set = nfnl_nft_set_alloc();
+	if (!set)
+		return -NLE_NOMEM;
+
+	set->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFTA_SET_MAX,
+			  nft_set_policy);
+	if (err < 0)
+		goto errout;
+
+	nfnl_nft_set_set_family(set, nfnlmsg_family(nlh));
+
+	nla = tb[NFTA_SET_TABLE];
+	if (nla != NULL) {
+		err = nfnl_nft_set_set_table(set, nla_data(nla), nla_len(nla));
+		if (err < 0)
+			goto errout;
+	}
+
+	nla = tb[NFTA_SET_NAME];
+	if (nla != NULL) {
+		err = nfnl_nft_set_set_name(set, nla_data(nla), nla_len(nla));
+		if (err < 0)
+			goto errout;
+	}
+
+	if (tb[NFTA_SET_FLAGS] != NULL)
+		nfnl_nft_set_set_flags(set, ntohl(nla_get_u32(tb[NFTA_SET_FLAGS])));
+	if (tb[NFTA_SET_KEY_TYPE] != NULL)
+		nfnl_nft_set_set_keytype(set, ntohl(nla_get_u32(tb[NFTA_SET_KEY_TYPE])));
+	if (tb[NFTA_SET_KEY_LEN] != NULL)
+		nfnl_nft_set_set_keylen(set, ntohl(nla_get_u32(tb[NFTA_SET_KEY_LEN])));
+	if (tb[NFTA_SET_DATA_TYPE] != NULL)
+		nfnl_nft_set_set_datatype(set, ntohl(nla_get_u32(tb[NFTA_SET_DATA_TYPE])));
+	if (tb[NFTA_SET_DATA_LEN] != NULL)
+		nfnl_nft_set_set_datalen(set, ntohl(nla_get_u32(tb[NFTA_SET_DATA_LEN])));
+	*result = set;
+	return 0;
+
+errout:
+	nfnl_nft_set_put(set);
+	return err;
+}
+
+static int nft_set_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			      struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nfnl_nft_set *set;
+	int err;
+
+	err = nfnlmsg_nft_set_parse(nlh, &set);
+	if (err < 0)
+		goto errout;
+
+	err = pp->pp_cb((struct nl_object *) set, pp);
+
+errout:
+	nfnl_nft_set_put(set);
+	return err;
+}
+
+int nfnl_nft_set_dump_request(struct nl_sock *h, int family, const char *table)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if (nfnlmsg_put(msg, 0, 0, NFNL_SUBSYS_NFTABLES, NFT_MSG_GETSET,
+			NLM_F_DUMP, family, 0) < 0)
+		goto nla_put_failure;
+
+	if (table != NULL)
+		NLA_PUT_STRING(msg, NFTA_SET_TABLE, table);
+
+	return nl_send_auto_complete(h, msg);
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+static int nft_set_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+	return nfnl_nft_set_dump_request(h, c->c_iarg1, (char *)c->c_iarg2);
+}
+
+static int nfnl_nft_set_build_message(struct nfnl_nft_set *set,
+				      int cmd, int flags,
+				      struct nl_msg **result)
+{
+	struct nl_msg *msg;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_NFTABLES, cmd, flags,
+				   nfnl_nft_set_get_family(set), 0);
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if (nfnl_nft_set_test_table(set))
+		NLA_PUT_STRING(msg, NFTA_SET_TABLE, nfnl_nft_set_get_table(set));
+	if (nfnl_nft_set_test_name(set))
+		NLA_PUT_STRING(msg, NFTA_SET_NAME, nfnl_nft_set_get_name(set));
+	if (nfnl_nft_set_test_flags(set))
+		NLA_PUT_U32(msg, NFTA_SET_FLAGS, htonl(nfnl_nft_set_get_flags(set)));
+	if (nfnl_nft_set_test_keytype(set))
+		NLA_PUT_U32(msg, NFTA_SET_KEY_TYPE, htonl(nfnl_nft_set_get_keytype(set)));
+	if (nfnl_nft_set_test_keylen(set))
+		NLA_PUT_U32(msg, NFTA_SET_KEY_LEN, htonl(nfnl_nft_set_get_keylen(set)));
+	if (nfnl_nft_set_test_datatype(set))
+		NLA_PUT_U32(msg, NFTA_SET_DATA_TYPE, htonl(nfnl_nft_set_get_datatype(set)));
+	if (nfnl_nft_set_test_datalen(set))
+		NLA_PUT_U32(msg, NFTA_SET_DATA_LEN, htonl(nfnl_nft_set_get_datalen(set)));
+
+	*result = msg;
+	return 0;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+int nfnl_nft_set_build_add_request(struct nfnl_nft_set *set, int flags,
+				   struct nl_msg **result)
+{
+	return nfnl_nft_set_build_message(set, NFT_MSG_NEWSET,
+					  NLM_F_CREATE | flags, result);
+}
+
+int nfnl_nft_set_add(struct nl_sock *h, struct nfnl_nft_set *set, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	err = nfnl_nft_set_build_add_request(set, flags, &msg);
+	if (err < 0)
+		return err;
+
+	err = nl_send_auto_complete(h, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(h);
+}
+
+int nfnl_nft_set_build_delete_request(struct nfnl_nft_set *set, int flags,
+					struct nl_msg **result)
+{
+	return nfnl_nft_set_build_message(set, NFT_MSG_DELSET, flags, result);
+}
+
+int nfnl_nft_set_delete(struct nl_sock *h, struct nfnl_nft_set *set, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	err = nfnl_nft_set_build_delete_request(set, flags, &msg);
+	if (err < 0)
+		return err;
+
+	err = nl_send_auto_complete(h, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(h);
+}
+
+int nfnl_nft_set_build_query_request(struct nfnl_nft_set *set, int flags,
+				       struct nl_msg **result)
+{
+	return nfnl_nft_set_build_message(set, NFT_MSG_GETSET, flags, result);
+}
+
+int nfnl_nft_set_query(struct nl_sock *h, struct nfnl_nft_set *set, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	err = nfnl_nft_set_build_query_request(set, flags, &msg);
+	if (err < 0)
+		return err;
+
+	err = nl_send_auto_complete(h, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(h);
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+/**
+ * Build a set cache holding all nf_tables sets currently in the kernel
+ * @arg sock		netlink sock
+ * @arg family		address family
+ * @arg table		table
+ * @arg result
+ *
+ * Allocates a new cache, initializes it properly and updates it to
+ * contain all sets currently in the specified table.
+ *
+ * @note The caller is responsible for destroying and freeing the
+ *       cache after using it.
+ * @return The cache or NULL if an error has occured.
+ */
+int nfnl_nft_set_alloc_cache(struct nl_sock *sock, int family, const char *table,
+			     struct nl_cache **result)
+{
+	struct nl_cache *cache;
+	int err;
+
+	cache = nl_cache_alloc(&nfnl_nft_set_ops);
+	if (!cache)
+		return -NLE_NOMEM;
+
+	cache->c_iarg1 = family;
+	cache->c_iarg2 = (unsigned long)table;
+
+	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+		free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
+}
+
+/** @} */
+
+/**
+ * @name Set Addition
+ * @{
+ */
+
+/** @} */
+
+static struct nl_af_group nft_set_groups[] = {
+	{ AF_UNSPEC, NFNLGRP_NFTABLES },
+	{ END_OF_GROUP_LIST },
+};
+
+#define NFNLMSG_NFT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_NFTABLES, (type))
+static struct nl_cache_ops nfnl_nft_set_ops = {
+	.co_name		= "netfilter/nft_set",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_NEWSET), NL_ACT_NEW, "new" },
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_GETSET), NL_ACT_GET, "get" },
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_DELSET), NL_ACT_DEL, "del" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_groups		= nft_set_groups,
+	.co_request_update	= nft_set_request_update,
+	.co_msg_parser		= nft_set_msg_parser,
+	.co_obj_ops		= &nft_set_obj_ops,
+};
+
+static void __init nft_set_init(void)
+{
+	nl_cache_mngt_register(&nfnl_nft_set_ops);
+}
+
+static void __exit nft_set_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_nft_set_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/nft_set_expr.c b/lib/netfilter/nft_set_expr.c
deleted file mode 100644
index 5ee8c1f..0000000
--- a/lib/netfilter/nft_set_expr.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * lib/netfilter/nft_set_expr.c
- *
- *	This library is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU Lesser General Public
- *	License as published by the Free Software Foundation version 2.1
- *	of the License.
- *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
- *
- * Development of this code funded by Astaro AG (http://www.astaro.com/)
- */
-
-#include <sys/types.h>
-#include <linux/netfilter/nf_tables.h>
-
-#include <netlink-local.h>
-#include <netlink/attr.h>
-#include <netlink/netfilter/nfnl.h>
-#include <netlink/netfilter/nft_data.h>
-#include <netlink/netfilter/nft_rule.h>
-#include <netlink/netfilter/nft_expr.h>
-#include <netlink/netfilter/nft-expr-modules.h>
-
-struct nft_set_expr {
-	struct nl_list_head	set_elements;
-	enum nft_registers	sreg;
-	enum nft_registers	dreg;
-	uint32_t		flags;
-	uint32_t		klen;
-	uint32_t		dlen;
-};
-
-struct nft_set_elem {
-	struct nl_list_head	list;
-	struct nfnl_nft_data *	key;
-	struct nfnl_nft_data *	data;
-	enum nft_set_elem_flags	flags;
-};
-
-static struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
-	[NFTA_SET_FLAGS]		= { .type = NLA_U32 },
-	[NFTA_SET_SREG]			= { .type = NLA_U32 },
-	[NFTA_SET_DREG]			= { .type = NLA_U32 },
-	[NFTA_SET_KLEN]			= { .type = NLA_U32 },
-	[NFTA_SET_DLEN]			= { .type = NLA_U32 },
-	[NFTA_SET_ELEMENTS]		= { .type = NLA_NESTED },
-};
-
-static struct nla_policy nft_se_policy[NFTA_SE_MAX + 1] = {
-	[NFTA_SE_KEY]			= { .type = NLA_NESTED },
-	[NFTA_SE_DATA]			= { .type = NLA_NESTED },
-	[NFTA_SE_FLAGS]			= { .type = NLA_U32 },
-};
-
-static struct nft_expr_ops set_expr_ops;
-
-static struct nft_set_expr *nft_set(const struct nfnl_nft_expr *expr)
-{
-	return (struct nft_set_expr *) expr->expr_data;
-}
-
-static struct nft_set_expr *nft_set_expr_alloc(struct nfnl_nft_expr *expr)
-{
-	struct nft_set_expr *set;
-
-	if (!expr->expr_data) {
-		set = calloc(1, sizeof(struct nft_set_expr));
-		if (set) {
-			memset(set, 0, sizeof(*set));
-			nl_init_list_head(&set->set_elements);
-			expr->expr_data = set;
-			expr->expr_ops = &set_expr_ops;
-		}
-	}
-
-	return nft_set(expr);
-}
-
-static void nft_set_free_data(struct nfnl_nft_expr *expr)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem, *next;
-
-	nl_list_for_each_entry_safe(elem, next, &set->set_elements, list) {
-		nfnl_nft_data_free(elem->key);
-		if (elem->data != NULL)
-			nfnl_nft_data_free(elem->data);
-		free(elem);
-	}
-}
-
-static int nft_set_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
-{
-	struct nft_set_expr *set;
-	struct nft_set_elem *elem;
-	struct nlattr *nla;
-	struct nlattr *se[NFTA_SE_MAX + 1];
-	int rem;
-
-	set = nft_set_expr_alloc(expr);
-	if (set == NULL)
-		return -ENOMEM;
-
-	if (tb[NFTA_SET_FLAGS] != NULL)
-		set->flags = ntohl(nla_get_u32(tb[NFTA_SET_FLAGS]));
-	if (tb[NFTA_SET_SREG] != NULL)
-		set->sreg = ntohl(nla_get_u32(tb[NFTA_SET_SREG]));
-	if (tb[NFTA_SET_DREG] != NULL)
-		set->dreg = ntohl(nla_get_u32(tb[NFTA_SET_DREG]));
-	if (tb[NFTA_SET_KLEN] != NULL)
-		set->klen = ntohl(nla_get_u32(tb[NFTA_SET_KLEN]));
-	if (tb[NFTA_SET_DLEN] != NULL)
-		set->dlen = ntohl(nla_get_u32(tb[NFTA_SET_DLEN]));
-
-	if (tb[NFTA_SET_ELEMENTS] != NULL) {
-		nla_for_each_nested(nla, tb[NFTA_SET_ELEMENTS], rem) {
-			if (nla_type(nla) != NFTA_LIST_ELEM)
-				goto errout;
-			if (nla_parse_nested(se, NFTA_SE_MAX, nla, nft_se_policy) < 0)
-				goto errout;
-
-			elem = malloc(sizeof(*elem));
-			if (elem == NULL)
-				goto errout;
-			memset(elem, 0, sizeof(*elem));
-
-			if (se[NFTA_SE_KEY] == NULL)
-				goto errout;
-			if (nfnl_nft_data_parse(se[NFTA_SE_KEY], &elem->key) < 0)
-				goto errout;
-			if (se[NFTA_SE_DATA] != NULL) {
-				if (nfnl_nft_data_parse(se[NFTA_SE_DATA], &elem->data) < 0)
-					goto errout;
-			}
-			if (se[NFTA_SE_FLAGS] != NULL)
-				elem->flags = ntohl(nla_get_u32(se[NFTA_SE_FLAGS]));
-
-			nl_list_add_tail(&elem->list, &set->set_elements);
-		}
-	}
-
-	return 0;
-
-errout:
-	return -1;
-}
-
-static int nft_set_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem;
-	struct nlattr *list, *nest;
-
-	if (nla_put_u32(msg, NFTA_SET_FLAGS, htonl(set->flags)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_SET_SREG, htonl(set->sreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_SET_KLEN, htonl(set->klen)) < 0)
-		goto errout;
-
-	if (set->flags & NFT_SET_MAP) {
-		if (nla_put_u32(msg, NFTA_SET_DREG, htonl(set->dreg)) < 0)
-			goto errout;
-		if (nla_put_u32(msg, NFTA_SET_DLEN, htonl(set->dlen)) < 0)
-			goto errout;
-	}
-
-	list = nla_nest_start(msg, NFTA_SET_ELEMENTS);
-	if (list == NULL)
-		goto errout;
-	nl_list_for_each_entry(elem, &set->set_elements, list) {
-		nest = nla_nest_start(msg, NFTA_LIST_ELEM);
-		if (nest == NULL)
-			goto errout;
-		if (nfnl_nft_data_put(msg, NFTA_SE_KEY, elem->key) < 0)
-			goto errout;
-
-		if (set->flags & NFT_SET_MAP &&
-		    !(elem->flags & NFT_SE_INTERVAL_END) &&
-		    nfnl_nft_data_put(msg, NFTA_SE_DATA, elem->data) < 0)
-		    	goto errout;
-
-		if (elem->flags &&
-		    nla_put_u32(msg, NFTA_SE_FLAGS, htonl(elem->flags)) < 0)
-			goto errout;
-
-		nla_nest_end(msg, nest);
-	}
-	nla_nest_end(msg, list);
-	return 0;
-
-errout:
-	return -1;
-}
-
-static void nft_set_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem;
-	char *delim;
-
-	if (set->flags & NFT_SET_MAP)
-		nl_dump(p, "map %s%ub/reg %u load %ub reg %u\n",
-			set->flags & NFT_SET_INTERVAL ? "interval " : "",
-			set->klen, set->sreg, set->dlen, set->dreg);
-	else
-		nl_dump(p, "lookup %s%ub/reg %u\n",
-			set->flags & NFT_SET_INTERVAL ? "interval " : "",
-			set->klen, set->sreg);
-
-	delim = "    { ";
-	nl_list_for_each_entry(elem, &set->set_elements, list) {
-		nl_dump(p, "%s", delim);
-		nfnl_nft_data_dump(p, elem->key);
-		if (elem->data) {
-			nl_dump(p, " : ");
-			nfnl_nft_data_dump(p, elem->data);
-		} else if (elem->flags & NFT_SE_INTERVAL_END)
-			nl_dump(p, "[end]");
-		delim = ",\n      ";
-	}
-	nl_dump(p, " }");
-}
-
-int nfnl_nft_set_init(struct nfnl_nft_expr *expr)
-{
-	if (nft_set_expr_alloc(expr) == NULL)
-		return -1;
-	return 0;
-}
-
-void nfnl_nft_set_set_flags(struct nfnl_nft_expr *expr, uint32_t flags)
-{
-	nft_set(expr)->flags |= flags;
-}
-
-uint32_t nfnl_nft_set_get_flags(const struct nfnl_nft_expr *expr)
-{
-	return nft_set(expr)->flags;
-}
-
-void nfnl_nft_set_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
-{
-	nft_set(expr)->sreg = reg;
-}
-
-enum nft_registers nfnl_nft_set_get_sreg(const struct nfnl_nft_expr *expr)
-{
-	return nft_set(expr)->sreg;
-}
-
-void nfnl_nft_set_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
-{
-	nft_set(expr)->dreg = reg;
-}
-
-enum nft_registers nfnl_nft_set_get_dreg(const struct nfnl_nft_expr *expr)
-{
-	return nft_set(expr)->dreg;
-}
-
-void nfnl_nft_set_set_klen(struct nfnl_nft_expr *expr, unsigned int klen)
-{
-	nft_set(expr)->klen = klen;
-}
-
-unsigned int nfnl_nft_set_get_klen(const struct nfnl_nft_expr *expr)
-{
-	return nft_set(expr)->klen;
-}
-
-void nfnl_nft_set_set_dlen(struct nfnl_nft_expr *expr, unsigned int dlen)
-{
-	nft_set(expr)->dlen = dlen;
-}
-
-unsigned int nfnl_nft_set_get_dlen(const struct nfnl_nft_expr *expr)
-{
-	return nft_set(expr)->dlen;
-}
-
-int nfnl_nft_set_add_mapping(struct nfnl_nft_expr *expr,
-			     struct nfnl_nft_data *key,
-			     struct nfnl_nft_data *data,
-			     enum nft_set_elem_flags flags)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem;
-
-	if (data != NULL && !(set->flags & NFT_SET_MAP))
-		return -1;
-
-	elem = malloc(sizeof(*elem));
-	if (elem == NULL)
-		return -1;
-
-	elem->key   = key;
-	elem->data  = data;
-	elem->flags = flags;
-	nl_list_add_tail(&elem->list, &set->set_elements);
-	return 0;
-}
-
-int nfnl_nft_set_add_elem(struct nfnl_nft_expr *expr, struct nfnl_nft_data *data,
-			  enum nft_set_elem_flags flags)
-{
-	return nfnl_nft_set_add_mapping(expr, data, NULL, flags);
-}
-
-void nfnl_nft_set_foreach_elem(const struct nfnl_nft_expr *expr,
-			       void (*cb)(struct nfnl_nft_data *,
-				       	  enum nft_set_elem_flags, void *),
-			       void *arg)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem;
-
-	nl_list_for_each_entry(elem, &set->set_elements, list)
-		cb(elem->key, elem->flags, arg);
-}
-
-void nfnl_nft_set_foreach_mapping(const struct nfnl_nft_expr *expr,
-				  void (*cb)(struct nfnl_nft_data *,
-				  	     struct nfnl_nft_data *,
-					     enum nft_set_elem_flags, void *),
-				  void *arg)
-{
-	struct nft_set_expr *set = nft_set(expr);
-	struct nft_set_elem *elem;
-
-	nl_list_for_each_entry(elem, &set->set_elements, list)
-		cb(elem->key, elem->data, elem->flags, arg);
-}
-
-static struct trans_tbl set_flags[] = {
-	__ADD(NFT_SET_INTERVAL,		interval)
-	__ADD(NFT_SET_MAP,		map)
-};
-
-char *nfnl_nft_set_flags2str(enum nft_set_flags flags, char *buf, size_t len)
-{
-	return __flags2str(flags, buf, len, set_flags, ARRAY_SIZE(set_flags));
-}
-
-enum nft_set_flags nfnl_nft_set_str2flags(const char *name)
-{
-	return __str2flags(name, set_flags, ARRAY_SIZE(set_flags));
-}
-
-static struct nft_expr_ops set_expr_ops = {
-	.eo_name		= "set",
-	.eo_dump[NL_DUMP_DETAILS]	= nft_set_dump,
-	.eo_get_opts		= nft_set_get_opts,
-	.eo_msg_parser		= nft_set_msg_parser,
-	.eo_free_data		= nft_set_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_set_policy,
-	.eo_maxattr		= NFTA_SET_MAX,
-};
-
-static void __init nft_set_expr_init(void)
-{
-	nft_expr_register(&set_expr_ops);
-}
-
-static void __exit nft_set_expr_exit(void)
-{
-	nft_expr_unregister(&set_expr_ops);
-}
-
-/** @} */
diff --git a/lib/netfilter/nft_set_obj.c b/lib/netfilter/nft_set_obj.c
new file mode 100644
index 0000000..c74a469
--- /dev/null
+++ b/lib/netfilter/nft_set_obj.c
@@ -0,0 +1,346 @@
+/*
+ * lib/netfilter/nft_set_obj.c
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/nft_table.h>
+#include <netlink/netfilter/nft_set.h>
+
+/** @cond SKIP */
+#define SET_ATTR_FAMILY		(1UL << 0)
+#define SET_ATTR_TABLE		(1UL << 1)
+#define SET_ATTR_NAME		(1UL << 2)
+#define SET_ATTR_FLAGS		(1UL << 3)
+#define SET_ATTR_KEYTYPE	(1UL << 4)
+#define SET_ATTR_KEYLEN		(1UL << 5)
+#define SET_ATTR_DATATYPE	(1UL << 6)
+#define SET_ATTR_DATALEN	(1UL << 7)
+/** @endcond */
+
+static void nft_set_free_data(struct nl_object *obj)
+{
+	struct nfnl_nft_set *set = nl_object_priv(obj);
+
+	if (set == NULL)
+		return;
+
+	nl_data_free(set->set_table);
+	nl_data_free(set->set_name);
+}
+
+static int nft_set_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_nft_set *dst = nl_object_priv(_dst);
+	struct nfnl_nft_set *src = nl_object_priv(_src);
+	struct nl_data *table = NULL, *name = NULL;
+
+	if (src->set_table != NULL) {
+		table = nl_data_clone(src->set_table);
+		if (table == NULL)
+			goto err1;
+	}
+
+	if (src->set_name != NULL) {
+		name = nl_data_clone(src->set_name);
+		if (name == NULL)
+			goto err2;
+	}
+
+	dst->set_table = table;
+	dst->set_name  = name;
+	return 0;
+
+err2:
+	nl_data_free(table);
+err1:
+	return -NLE_NOMEM;
+}
+
+static void nft_set_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+	struct nfnl_nft_set *set = nl_object_priv(obj);
+	char buf[64];
+
+	if (set->ce_mask & SET_ATTR_NAME) {
+		nl_dump(p, "%s %s",
+			set->set_flags & NFT_SET_MAP ? "map" : "set",
+			nfnl_nft_set_get_name(set));
+
+		if (set->ce_mask & SET_ATTR_TABLE)
+			nl_dump(p, "@%s/%s",
+				nfnl_nft_set_get_table(set),
+				nl_af2str(nfnl_nft_set_get_family(set),
+					  buf, sizeof(buf)));
+
+		if (set->ce_mask & SET_ATTR_FLAGS) {
+			nfnl_nft_set_flags2str(set->set_flags, buf, sizeof(buf));
+			nl_dump(p, " <%s>", buf);
+		}
+
+		nl_dump(p, "\n");
+	}
+
+	if (set->ce_mask & (SET_ATTR_KEYTYPE | SET_ATTR_KEYLEN)) {
+		nl_dump(p, "\tkey: ");
+		if (set->ce_mask & SET_ATTR_KEYTYPE)
+			nl_dump(p, "type %x ", set->set_keytype);
+		if (set->ce_mask & SET_ATTR_KEYLEN)
+			nl_dump(p, "len %u ", set->set_keylen);
+		nl_dump(p, "\n");
+	}
+
+	if (set->ce_mask & (SET_ATTR_DATATYPE | SET_ATTR_DATALEN)) {
+		nl_dump(p, "\tdata: ");
+		if (set->ce_mask & SET_ATTR_DATATYPE)
+			nl_dump(p, "type %x ", set->set_datatype);
+		if (set->ce_mask & SET_ATTR_DATALEN)
+			nl_dump(p, "len %u ", set->set_datalen);
+		nl_dump(p, "\n");
+	}
+}
+
+static int nft_set_compare(struct nl_object *_a, struct nl_object *_b,
+			   uint32_t attrs, int flags)
+{
+	// FIXME
+	return 0;
+}
+
+static struct trans_tbl nft_set_attrs[] = {
+	__ADD(SET_ATTR_FAMILY,			family)
+	__ADD(SET_ATTR_TABLE,			table)
+	__ADD(SET_ATTR_NAME,			name)
+	__ADD(SET_ATTR_FLAGS,			flags)
+	__ADD(SET_ATTR_KEYTYPE,			keytype)
+	__ADD(SET_ATTR_KEYLEN,			keylen)
+	__ADD(SET_ATTR_DATATYPE,		datatype)
+	__ADD(SET_ATTR_DATALEN,			datalen)
+};
+
+static char *nft_set_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_set_attrs,
+			   ARRAY_SIZE(nft_set_attrs));
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_nft_set *nfnl_nft_set_alloc(void)
+{
+	return (struct nfnl_nft_set *) nl_object_alloc(&nft_set_obj_ops);
+}
+
+void nfnl_nft_set_get(struct nfnl_nft_set *set)
+{
+	nl_object_get((struct nl_object *) set);
+}
+
+void nfnl_nft_set_put(struct nfnl_nft_set *set)
+{
+	nl_object_put((struct nl_object *) set);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_nft_set_set_family(struct nfnl_nft_set *set, uint8_t family)
+{
+	set->set_family = family;
+	set->ce_mask |= SET_ATTR_FAMILY;
+}
+
+int nfnl_nft_set_test_family(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_FAMILY);
+}
+
+uint8_t nfnl_nft_set_get_family(const struct nfnl_nft_set *set)
+{
+	return set->set_family;
+}
+
+int nfnl_nft_set_set_table(struct nfnl_nft_set *set, const char *table, int len)
+{
+	struct nl_data *data;
+
+	data = nl_data_alloc(table, len);
+	if (data == NULL)
+		return -NLE_NOMEM;
+
+	if (set->set_table)
+		nl_data_free(set->set_table);
+	set->set_table = data;
+	set->ce_mask |= SET_ATTR_TABLE;
+	return 0;
+}
+
+int nfnl_nft_set_test_table(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_TABLE);
+}
+
+const char *nfnl_nft_set_get_table(const struct nfnl_nft_set *set)
+{
+	return nl_data_get(set->set_table);
+}
+
+int nfnl_nft_set_set_name(struct nfnl_nft_set *set, const char *name, int len)
+{
+	struct nl_data *data;
+
+	data = nl_data_alloc(name, len);
+	if (data == NULL)
+		return -NLE_NOMEM;
+
+	if (set->set_name)
+		nl_data_free(set->set_name);
+	set->set_name = data;
+	set->ce_mask |= SET_ATTR_NAME;
+	return 0;
+}
+
+int nfnl_nft_set_test_name(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_NAME);
+}
+
+const char *nfnl_nft_set_get_name(const struct nfnl_nft_set *set)
+{
+	return nl_data_get(set->set_name);
+}
+
+void nfnl_nft_set_set_flags(struct nfnl_nft_set *set, uint32_t flags)
+{
+	set->set_flags |= flags;
+	set->ce_mask |= SET_ATTR_FLAGS;
+}
+
+int nfnl_nft_set_test_flags(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_FLAGS);
+}
+
+uint32_t nfnl_nft_set_get_flags(const struct nfnl_nft_set *set)
+{
+	return set->set_flags;
+}
+
+void nfnl_nft_set_set_keytype(struct nfnl_nft_set *set, enum nft_data_types type)
+{
+	set->set_keytype = type;
+	set->ce_mask |= SET_ATTR_KEYTYPE;
+}
+
+int nfnl_nft_set_test_keytype(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_KEYTYPE);
+}
+
+enum nft_data_types nfnl_nft_set_get_keytype(const struct nfnl_nft_set *set)
+{
+	return set->set_keytype;
+}
+
+void nfnl_nft_set_set_keylen(struct nfnl_nft_set *set, unsigned int len)
+{
+	set->set_keylen = len;
+	set->ce_mask |= SET_ATTR_KEYLEN;
+}
+
+int nfnl_nft_set_test_keylen(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_KEYLEN);
+}
+
+unsigned int nfnl_nft_set_get_keylen(const struct nfnl_nft_set *set)
+{
+	return set->set_keylen;
+}
+
+void nfnl_nft_set_set_datatype(struct nfnl_nft_set *set, enum nft_data_types type)
+{
+	set->set_datatype = type;
+	set->ce_mask |= SET_ATTR_DATATYPE;
+}
+
+int nfnl_nft_set_test_datatype(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_DATATYPE);
+}
+
+enum nft_data_types nfnl_nft_set_get_datatype(const struct nfnl_nft_set *set)
+{
+	return set->set_datatype;
+}
+
+void nfnl_nft_set_set_datalen(struct nfnl_nft_set *set, unsigned int len)
+{
+	set->set_datalen = len;
+	set->ce_mask |= SET_ATTR_DATALEN;
+}
+
+int nfnl_nft_set_test_datalen(const struct nfnl_nft_set *set)
+{
+	return !!(set->ce_mask & SET_ATTR_DATALEN);
+}
+
+unsigned int nfnl_nft_set_get_datalen(const struct nfnl_nft_set *set)
+{
+	return set->set_datalen;
+}
+
+static struct trans_tbl set_flags[] = {
+	__ADD(NFT_SET_ANONYMOUS,	anonymous)
+	__ADD(NFT_SET_CONSTANT,		constant)
+	__ADD(NFT_SET_INTERVAL,		interval)
+	__ADD(NFT_SET_MAP,		map)
+};
+
+char *nfnl_nft_set_flags2str(uint32_t flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, set_flags, ARRAY_SIZE(set_flags));
+}
+
+uint32_t nfnl_nft_set_str2flags(const char *name)
+{
+	return __str2flags(name, set_flags, ARRAY_SIZE(set_flags));
+}
+
+/** @} */
+
+struct nl_object_ops nft_set_obj_ops = {
+	.oo_name		= "netfilter/nft_set",
+	.oo_size		= sizeof(struct nfnl_nft_set),
+	.oo_free_data		= nft_set_free_data,
+	.oo_clone		= nft_set_clone,
+	.oo_dump = {
+		[NL_DUMP_LINE]		= nft_set_dump,
+		[NL_DUMP_DETAILS]	= nft_set_dump,
+		[NL_DUMP_STATS]		= nft_set_dump,
+	},
+	.oo_compare		= nft_set_compare,
+	.oo_attrs2str		= nft_set_attrs2str,
+};
+
+/** @} */
diff --git a/lib/netfilter/nft_setelem.c b/lib/netfilter/nft_setelem.c
new file mode 100644
index 0000000..f305005
--- /dev/null
+++ b/lib/netfilter/nft_setelem.c
@@ -0,0 +1,391 @@
+/*
+ * lib/netfilter/nft_setelem.c	nf_tables set elements
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup nft nf_tables
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/nft_set.h>
+#include <netlink/netfilter/nft_setelem.h>
+
+static struct nl_cache_ops nfnl_nft_setelem_ops;
+
+static struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
+	[NFTA_SET_ELEM_KEY]		= { .type = NLA_NESTED },
+	[NFTA_SET_ELEM_DATA]		= { .type = NLA_NESTED },
+	[NFTA_SET_ELEM_FLAGS]		= { .type = NLA_U32 },
+};
+
+static struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
+	[NFTA_SET_ELEM_LIST_TABLE]	= { .type = NLA_STRING },
+	[NFTA_SET_ELEM_LIST_SET]	= { .type = NLA_STRING },
+	[NFTA_SET_ELEM_LIST_ELEMENTS]	= { .type = NLA_NESTED },
+};
+
+static int nft_set_elem_parse(struct nlmsghdr *nlh, struct nlattr *nla,
+			      struct nfnl_nft_setelem **result)
+{
+	struct nlattr *tb[NFTA_SET_ELEM_MAX + 1];
+	struct nfnl_nft_setelem *elem;
+	struct nfnl_nft_data *data;
+	uint32_t flags;
+	int err;
+
+	err = nla_parse_nested(tb, NFTA_SET_ELEM_MAX, nla, nft_set_elem_policy);
+	if (err < 0)
+		return err;
+
+	elem = nfnl_nft_setelem_alloc();
+	if (elem == NULL)
+		return -NLE_NOMEM;
+
+	elem->ce_msgtype = nlh->nlmsg_type;
+
+	if (tb[NFTA_SET_ELEM_KEY] != NULL) {
+		err = nfnl_nft_data_parse(tb[NFTA_SET_ELEM_KEY], &data);
+		if (err < 0)
+			goto err1;
+		nfnl_nft_setelem_set_key(elem, data);
+	}
+	if (tb[NFTA_SET_ELEM_DATA] != NULL) {
+		err = nfnl_nft_data_parse(tb[NFTA_SET_ELEM_DATA], &data);
+		if (err < 0)
+			goto err1;
+		nfnl_nft_setelem_set_data(elem, data);
+	}
+	if (tb[NFTA_SET_ELEM_FLAGS] != NULL) {
+		flags = ntohl(nla_get_u32(tb[NFTA_SET_ELEM_FLAGS]));
+		nfnl_nft_setelem_set_flags(elem, flags);
+	}
+
+	*result = elem;
+	return 0;
+
+err1:
+	nfnl_nft_setelem_put(elem);
+	return err;
+}
+
+static int nfnlmsg_nft_setelem_parse(struct nlmsghdr *nlh, struct nl_list_head *list)
+{
+	struct nlattr *tb[NFTA_SET_ELEM_LIST_MAX + 1], *nla;
+	struct nfnl_nft_setelem *elem;
+	int rem, err;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb,
+			  NFTA_SET_ELEM_LIST_MAX,
+			  nft_set_elem_list_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
+		goto err1;
+	nla_for_each_nested(nla, tb[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+		err = nft_set_elem_parse(nlh, nla, &elem);
+		if (err < 0)
+			goto err1;
+		nl_list_add_tail(&elem->elem_list, list);
+	}
+	return 0;
+
+err1:
+	return err;
+}
+
+static int nft_setelem_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+				  struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nfnl_nft_setelem *elem, *next;
+	NL_LIST_HEAD(list);
+	int err;
+
+	err = nfnlmsg_nft_setelem_parse(nlh, &list);
+
+	nl_list_for_each_entry_safe(elem, next, &list, elem_list) {
+		nl_list_del(&elem->elem_list);
+		if (err >= 0)
+			err = pp->pp_cb((struct nl_object *) elem, pp);
+		else
+			nfnl_nft_setelem_put(elem);
+	}
+
+	return err;
+}
+
+int nfnl_nft_setelem_dump_request(struct nl_sock *h, struct nfnl_nft_set *set)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if (nfnlmsg_put(msg, 0, 0, NFNL_SUBSYS_NFTABLES, NFT_MSG_GETSETELEM,
+			NLM_F_DUMP, set->set_family, 0) < 0)
+		goto nla_put_failure;
+
+	if (nfnl_nft_set_test_table(set))
+		NLA_PUT_STRING(msg, NFTA_SET_ELEM_LIST_TABLE,
+			       nfnl_nft_set_get_table(set));
+	if (nfnl_nft_set_test_name(set))
+		NLA_PUT_STRING(msg, NFTA_SET_ELEM_LIST_SET,
+			       nfnl_nft_set_get_name(set));
+
+	return nl_send_auto_complete(h, msg);
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+static int nft_setelem_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+	return nfnl_nft_setelem_dump_request(h, (struct nfnl_nft_set *)c->c_iarg1);
+}
+
+static int nfnl_nft_setelem_build(struct nl_msg *msg,
+				  const struct nfnl_nft_setelem *elem)
+{
+	unsigned int size = msg->nm_size;
+	struct nlattr *nest;
+	int err;
+
+	nest = nla_nest_start(msg, NFTA_LIST_ELEM);
+	if (nest == NULL)
+		goto nla_put_failure;
+
+	if (elem->elem_key != NULL) {
+		err = nfnl_nft_data_put(msg, NFTA_SET_ELEM_KEY, elem->elem_key);
+		if (err < 0)
+			goto nla_put_failure;
+	}
+	if (elem->elem_data != NULL) {
+		err = nfnl_nft_data_put(msg, NFTA_SET_ELEM_DATA, elem->elem_data);
+		if (err < 0)
+			goto nla_put_failure;
+	}
+	if (elem->elem_flags != 0)
+		NLA_PUT_U32(msg, NFTA_SET_ELEM_FLAGS, htonl(elem->elem_flags));
+
+	nla_nest_end(msg, nest);
+	return 0;
+
+nla_put_failure:
+	msg->nm_size = size;
+	return -NLE_MSGSIZE;
+}
+
+static int nfnl_nft_setelem_build_message(struct nfnl_nft_set *set,
+					  struct nl_cache *elements,
+					  struct nl_object **last,
+					  int cmd, int flags,
+					  struct nl_msg **result)
+{
+	struct nl_object *obj;
+	struct nlattr *nest;
+	struct nl_msg *msg;
+
+	obj = last != NULL ? *last : NULL;
+	obj = nl_list_prepare_entry(obj, &elements->c_items, ce_list);
+	if (obj->ce_list.next == &elements->c_items)
+		return 0;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_NFTABLES, cmd, flags,
+				   nfnl_nft_set_get_family(set), 0);
+	if (msg == NULL)
+		return -NLE_NOMEM;
+
+	if (nfnl_nft_set_test_table(set))
+		NLA_PUT_STRING(msg, NFTA_SET_ELEM_LIST_TABLE,
+			       nfnl_nft_set_get_table(set));
+	if (nfnl_nft_set_test_name(set))
+		NLA_PUT_STRING(msg, NFTA_SET_ELEM_LIST_SET,
+			       nfnl_nft_set_get_name(set));
+
+	nest = nla_nest_start(msg, NFTA_SET_ELEM_LIST_ELEMENTS);
+	if (nest == NULL)
+		goto nla_put_failure;
+
+	nl_list_for_each_entry_continue(obj, &elements->c_items, ce_list) {
+		if (nfnl_nft_setelem_build(msg, nl_object_priv(obj)) < 0)
+			break;
+		if (last != NULL)
+			*last = obj;
+	}
+	nla_nest_end(msg, nest);
+
+	*result = msg;
+	return msg->nm_size;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -NLE_MSGSIZE;
+}
+
+static int nfnl_nft_setelem_send_messages(struct nl_sock *h,
+					  struct nfnl_nft_set *set,
+					  struct nl_cache *elements,
+					  int cmd, int flags)
+{
+	struct nl_object *obj = NULL;
+	struct nl_msg *msg;
+	int err;
+
+	while (1) {
+		err = nfnl_nft_setelem_build_message(set, elements, &obj,
+						     cmd, flags, &msg);
+		if (err <= 0)
+			break;
+
+		err = nl_send_auto_complete(h, msg);
+		nlmsg_free(msg);
+		if (err < 0)
+			break;
+		err = wait_for_ack(h);
+		if (err < 0)
+			break;
+	}
+	return err;
+}
+
+int nfnl_nft_setelem_add(struct nl_sock *h, struct nfnl_nft_set *set,
+			 struct nl_cache *elements, int flags)
+{
+	return nfnl_nft_setelem_send_messages(h, set, elements,
+					      NFT_MSG_NEWSETELEM,
+					      NLM_F_CREATE | flags);
+}
+
+int nfnl_nft_setelem_build_delete_request(struct nfnl_nft_set *set, int flags,
+					  struct nl_msg **result)
+{
+	return nfnl_nft_setelem_build_message(set, NULL, NULL, NFT_MSG_DELSETELEM,
+					      flags, result);
+}
+
+int nfnl_nft_setelem_delete(struct nl_sock *h, struct nfnl_nft_set *set,
+			    struct nl_cache *elements, int flags)
+{
+	return nfnl_nft_setelem_send_messages(h, set, elements,
+					      NFT_MSG_DELSETELEM,
+					      flags);
+}
+
+int nfnl_nft_setelem_build_query_request(struct nfnl_nft_set *set, int flags,
+					 struct nl_msg **result)
+{
+	return nfnl_nft_setelem_build_message(set, NULL, NULL, NFT_MSG_GETSETELEM,
+					      flags, result);
+}
+
+int nfnl_nft_setelem_query(struct nl_sock *h, struct nfnl_nft_set *set, int flags)
+{
+	struct nl_msg *msg;
+	int err;
+
+	err = nfnl_nft_set_build_query_request(set, flags, &msg);
+	if (err < 0)
+		return err;
+
+	err = nl_send_auto_complete(h, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return wait_for_ack(h);
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+/**
+ * Build a set cache holding all nf_tables sets currently in the kernel
+ * @arg sock		netlink sock
+ * @arg set		nftables set
+ * @arg result
+ *
+ * Allocates a new cache, initializes it properly and updates it to
+ * contain all elements currently in the specified set.
+ *
+ * @note The caller is responsible for destroying and freeing the
+ *       cache after using it.
+ * @return The cache or NULL if an error has occured.
+ */
+int nfnl_nft_setelem_alloc_cache(struct nl_sock *sock, struct nfnl_nft_set *set,
+				 struct nl_cache **result)
+{
+	struct nl_cache *cache;
+	int err;
+
+	cache = nl_cache_alloc(&nfnl_nft_setelem_ops);
+	if (!cache)
+		return -NLE_NOMEM;
+
+	cache->c_iarg1 = (unsigned long)set;
+
+	if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+		free(cache);
+		return err;
+	}
+
+	*result = cache;
+	return 0;
+}
+
+/** @} */
+
+/**
+ * @name Set Element Addition
+ * @{
+ */
+
+/** @} */
+
+#define NFNLMSG_NFT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_NFTABLES, (type))
+static struct nl_cache_ops nfnl_nft_setelem_ops = {
+	.co_name		= "netfilter/nft_setelem",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_NEWSETELEM), NL_ACT_NEW, "new" },
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_GETSETELEM), NL_ACT_GET, "get" },
+		{ NFNLMSG_NFT_TYPE(NFT_MSG_DELSETELEM), NL_ACT_DEL, "del" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_request_update	= nft_setelem_request_update,
+	.co_msg_parser		= nft_setelem_msg_parser,
+	.co_obj_ops		= &nft_setelem_obj_ops,
+};
+
+static void __init nft_setelem_init(void)
+{
+	nl_cache_mngt_register(&nfnl_nft_setelem_ops);
+}
+
+static void __exit nft_setelem_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_nft_setelem_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/nft_setelem_obj.c b/lib/netfilter/nft_setelem_obj.c
new file mode 100644
index 0000000..8c754cc
--- /dev/null
+++ b/lib/netfilter/nft_setelem_obj.c
@@ -0,0 +1,215 @@
+/*
+ * lib/netfilter/nft_setelem_obj.c
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/nft_table.h>
+#include <netlink/netfilter/nft_set.h>
+#include <netlink/netfilter/nft_setelem.h>
+
+/** @cond SKIP */
+#define SETELEM_ATTR_KEY	(1UL << 0)
+#define SETELEM_ATTR_DATA	(1UL << 1)
+#define SETELEM_ATTR_FLAGS	(1UL << 2)
+/** @endcond */
+
+static void nft_setelem_free_data(struct nl_object *obj)
+{
+	struct nfnl_nft_setelem *elem = nl_object_priv(obj);;
+
+	if (elem == NULL)
+		return;
+
+	if (elem->elem_key != NULL)
+		nfnl_nft_data_free(elem->elem_key);
+	if (elem->elem_data != NULL)
+		nfnl_nft_data_free(elem->elem_data);
+}
+
+static int nft_setelem_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_nft_set *dst = nl_object_priv(_dst);
+	struct nfnl_nft_set *src = nl_object_priv(_src);
+	struct nl_data *table = NULL, *name = NULL;
+
+	if (src->set_table != NULL) {
+		table = nl_data_clone(src->set_table);
+		if (table == NULL)
+			goto err1;
+	}
+
+	if (src->set_name != NULL) {
+		name = nl_data_clone(src->set_name);
+		if (name == NULL)
+			goto err2;
+	}
+
+	dst->set_table = table;
+	dst->set_name  = name;
+
+err2:
+	nl_data_free(table);
+err1:
+	return -NLE_NOMEM;
+}
+
+static void nft_setelem_dump(struct nl_object *obj, struct nl_dump_params *p)
+{
+	struct nfnl_nft_setelem *elem = nl_object_priv(obj);
+
+	nl_dump(p, "element ");
+	if (elem->ce_mask & SETELEM_ATTR_KEY)
+		nfnl_nft_data_dump(p, elem->elem_key);
+	if (elem->ce_mask & SETELEM_ATTR_DATA) {
+		nl_dump(p, " : ");
+		nfnl_nft_data_dump(p, elem->elem_data);
+	}
+	if (elem->ce_mask & SETELEM_ATTR_FLAGS) {
+		if (elem->elem_flags & NFT_SET_ELEM_INTERVAL_END)
+			nl_dump(p, "[end]");
+	}
+	nl_dump(p, "\n");
+}
+
+static int nft_setelem_compare(struct nl_object *_a, struct nl_object *_b,
+			       uint32_t attrs, int flags)
+{
+	// FIXME
+	return 0;
+}
+
+static struct trans_tbl nft_setelem_attrs[] = {
+	__ADD(SETELEM_ATTR_KEY,			key)
+	__ADD(SETELEM_ATTR_DATA,		data)
+	__ADD(SETELEM_ATTR_FLAGS,		flags)
+};
+
+static char *nft_setelem_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_setelem_attrs,
+			   ARRAY_SIZE(nft_setelem_attrs));
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_nft_setelem *nfnl_nft_setelem_alloc(void)
+{
+	return (struct nfnl_nft_setelem *) nl_object_alloc(&nft_setelem_obj_ops);
+}
+
+void nfnl_nft_setelem_get(struct nfnl_nft_setelem *elem)
+{
+	nl_object_get((struct nl_object *) elem);
+}
+
+void nfnl_nft_setelem_put(struct nfnl_nft_setelem *elem)
+{
+	nl_object_put((struct nl_object *) elem);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_nft_setelem_set_key(struct nfnl_nft_setelem *elem,
+			      struct nfnl_nft_data *key)
+{
+	elem->elem_key = key;
+	elem->ce_mask |= SETELEM_ATTR_KEY;
+}
+
+int nfnl_nft_setelem_test_key(const struct nfnl_nft_setelem *elem)
+{
+	return !!(elem->ce_mask & SETELEM_ATTR_KEY);
+}
+
+struct nfnl_nft_data *nfnl_nft_setelem_get_key(const struct nfnl_nft_setelem *elem)
+{
+	return elem->elem_key;
+}
+
+void nfnl_nft_setelem_set_data(struct nfnl_nft_setelem *elem,
+			       struct nfnl_nft_data *data)
+{
+	elem->elem_data = data;
+	elem->ce_mask |= SETELEM_ATTR_DATA;
+}
+
+int nfnl_nft_setelem_test_data(const struct nfnl_nft_setelem *elem)
+{
+	return !!(elem->ce_mask & SETELEM_ATTR_DATA);
+}
+
+struct nfnl_nft_data *nfnl_nft_setelem_get_data(const struct nfnl_nft_setelem *elem)
+{
+	return elem->elem_data;
+}
+
+void nfnl_nft_setelem_set_flags(struct nfnl_nft_setelem *elem, uint32_t flags)
+{
+	elem->elem_flags |= flags;
+	elem->ce_mask |= SETELEM_ATTR_FLAGS;
+}
+
+int nfnl_nft_setelem_test_flags(const struct nfnl_nft_setelem *elem)
+{
+	return !!(elem->ce_mask & SETELEM_ATTR_FLAGS);
+}
+
+uint32_t nfnl_nft_setelem_get_flags(const struct nfnl_nft_setelem *elem)
+{
+	return elem->elem_flags;
+}
+
+static struct trans_tbl setelem_flags[] = {
+	__ADD(NFT_SET_ELEM_INTERVAL_END,	interval_end)
+};
+
+char *nfnl_nft_setelem_flags2str(uint32_t flags, char *buf, size_t len)
+{
+	return __flags2str(flags, buf, len, setelem_flags,
+			   ARRAY_SIZE(setelem_flags));
+}
+
+uint32_t nfnl_nft_setelem_str2flags(const char *name)
+{
+	return __str2flags(name, setelem_flags, ARRAY_SIZE(setelem_flags));
+}
+
+/** @} */
+
+struct nl_object_ops nft_setelem_obj_ops = {
+	.oo_name		= "netfilter/nft_setelem",
+	.oo_size		= sizeof(struct nfnl_nft_setelem),
+	.oo_free_data		= nft_setelem_free_data,
+	.oo_clone		= nft_setelem_clone,
+	.oo_dump = {
+		[NL_DUMP_LINE]		= nft_setelem_dump,
+		[NL_DUMP_DETAILS]	= nft_setelem_dump,
+		[NL_DUMP_STATS]		= nft_setelem_dump,
+	},
+	.oo_compare		= nft_setelem_compare,
+	.oo_attrs2str		= nft_setelem_attrs2str,
+};
+
+/** @} */
diff --git a/src/.gitignore b/src/.gitignore
index b292567..3afc312 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -38,3 +38,6 @@ nf-rule-dump
 nf-table-add
 nf-table-del
 nf-table-dump
+nf-set-add
+nf-set-delete
+nf-set-dump
diff --git a/src/nf-set-add.c b/src/nf-set-add.c
new file mode 100644
index 0000000..499ad89
--- /dev/null
+++ b/src/nf-set-add.c
@@ -0,0 +1,118 @@
+/*
+ * src/nf-set-add.c     Add nf_tables set
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include "utils.h"
+#include <netlink-local.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/nft_set.h>
+#include <netlink/netfilter/nft_expr.h>
+#include <linux/ip.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nf-set-add --family <family> --table <table> --name <name> [ options ]\n"
+	"  options := { --keytype type | --keylen len | --flags flags | \n"
+	"               --datatype type | --datalen len }\n"
+	);
+	exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *nf_sock;
+	struct nfnl_nft_set *set;
+	int err = 1;
+
+	struct nl_dump_params params = {
+		.dp_fd		= stdout,
+		.dp_type	= NL_DUMP_DETAILS,
+	};
+
+	nf_sock = nlt_alloc_socket();
+	if (nlt_connect(nf_sock, NETLINK_NETFILTER) < 0)
+		return -1;
+
+	set = nfnl_nft_set_alloc();
+	if (set == NULL)
+		goto errout;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_TABLE,
+			ARG_NAME,
+			ARG_KEYTYPE,
+			ARG_KEYLEN,
+			ARG_FLAGS,
+			ARG_DATATYPE,
+			ARG_DATALEN,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "family", 1, 0, ARG_FAMILY, },
+			{ "table", 1, 0, ARG_TABLE, },
+			{ "name", 1, 0, ARG_NAME, },
+			{ "keytype", 1, 0, ARG_KEYTYPE },
+			{ "keylen", 1, 0, ARG_KEYLEN, },
+		};
+
+		c = getopt_long(argc, argv, "hf:t:n:k:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_usage();
+			exit(0);
+		case ARG_FAMILY:
+			nfnl_nft_set_set_family(set, nl_str2af(optarg));
+			break;
+		case ARG_TABLE:
+			nfnl_nft_set_set_table(set, optarg, strlen(optarg) + 1);
+			break;
+		case ARG_NAME:
+			nfnl_nft_set_set_name(set, optarg, strlen(optarg) + 1);
+			break;
+		case ARG_KEYTYPE:
+			nfnl_nft_set_set_keytype(set, strtoul(optarg, NULL, 0));
+			break;
+		case ARG_KEYLEN:
+			nfnl_nft_set_set_keylen(set, strtoul(optarg, NULL, 0));
+			break;
+		case ARG_FLAGS:
+			nfnl_nft_set_set_flags(set, nfnl_nft_set_str2flags(optarg));
+		case ARG_DATATYPE:
+			nfnl_nft_set_set_datatype(set, strtoul(optarg, NULL, 0));
+			break;
+		case ARG_DATALEN:
+			nfnl_nft_set_set_datalen(set, strtoul(optarg, NULL, 0));
+			break;
+		}
+	}
+
+	nl_object_dump((struct nl_object *) set, &params);
+	err = nfnl_nft_set_add(nf_sock, set, NLM_F_EXCL);
+	if (err < 0)
+		fprintf(stderr, "Unable to add set: %s\n",
+			nl_geterror(err));
+	else
+		err = 0;
+
+	nfnl_nft_set_put(set);
+errout:
+	return err;
+}
diff --git a/src/nf-set-delete.c b/src/nf-set-delete.c
new file mode 100644
index 0000000..749a57c
--- /dev/null
+++ b/src/nf-set-delete.c
@@ -0,0 +1,95 @@
+/*
+ * src/nf-set-add.c     Delete nf_tables set
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include "utils.h"
+#include <netlink-local.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/nft_set.h>
+#include <netlink/netfilter/nft_expr.h>
+#include <linux/ip.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nf-set-delete --family <family> --table <table> --name <name>\n"
+	);
+	exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *nf_sock;
+	struct nfnl_nft_set *set;
+	int err = 1;
+
+	struct nl_dump_params params = {
+		.dp_fd		= stdout,
+		.dp_type	= NL_DUMP_DETAILS,
+	};
+
+	nf_sock = nlt_alloc_socket();
+	if (nlt_connect(nf_sock, NETLINK_NETFILTER) < 0)
+		return -1;
+
+	set = nfnl_nft_set_alloc();
+	if (set == NULL)
+		goto errout;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_TABLE,
+			ARG_NAME,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "family", 1, 0, ARG_FAMILY, },
+			{ "table", 1, 0, ARG_TABLE, },
+			{ "name", 1, 0, ARG_NAME, },
+		};
+
+		c = getopt_long(argc, argv, "hf:t:n:k:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_usage();
+			exit(0);
+		case ARG_FAMILY:
+			nfnl_nft_set_set_family(set, nl_str2af(optarg));
+			break;
+		case ARG_TABLE:
+			nfnl_nft_set_set_table(set, optarg, strlen(optarg) + 1);
+			break;
+		case ARG_NAME:
+			nfnl_nft_set_set_name(set, optarg, strlen(optarg) + 1);
+			break;
+		}
+	}
+
+	nl_object_dump((struct nl_object *) set, &params);
+	err = nfnl_nft_set_delete(nf_sock, set, 0);
+	if (err < 0)
+		fprintf(stderr, "Unable to delete set: %s\n",
+			nl_geterror(err));
+	else
+		err = 0;
+
+	nfnl_nft_set_put(set);
+errout:
+	return err;
+}
diff --git a/src/nf-set-dump.c b/src/nf-set-dump.c
new file mode 100644
index 0000000..62de6fb
--- /dev/null
+++ b/src/nf-set-dump.c
@@ -0,0 +1,96 @@
+/*
+ * src/nf-set-dump.c     Dump nf_tables set attributes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include "utils.h"
+#include <netlink/netfilter/nft_set.h>
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nf-set-dump --family <family> --table <table> <mode>\n"
+	"  mode := { brief | detailed | stats | xml }\n"
+	);
+	exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_sock *nf_sock;
+	struct nl_cache *set_cache;
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_LINE,
+	};
+	int err = 1, family = AF_UNSPEC;
+	const char *table = NULL;
+
+	for (;;) {
+		int c, optidx = 0;
+		enum {
+			ARG_FAMILY = 257,
+			ARG_TABLE,
+		};
+		static struct option long_opts[] = {
+			{ "help", 0, 0, 'h' },
+			{ "family", 1, 0, ARG_FAMILY, },
+			{ "table", 1, 0, ARG_TABLE, },
+		};
+
+		c = getopt_long(argc, argv, "hf:t:", long_opts, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_usage();
+			break;
+		case ARG_FAMILY:
+			family = nl_str2af(optarg);
+			break;
+		case ARG_TABLE:
+			table = optarg;
+			break;
+		}
+	}
+
+	if (optind == argc) {
+		print_usage();
+		exit(1);
+	}
+	params.dp_type = nlt_parse_dumptype(argv[optind]);
+	if (params.dp_type < 0)
+		goto errout_cache;
+
+
+	nf_sock = nlt_alloc_socket();
+
+	if (nlt_connect(nf_sock, NETLINK_NETFILTER) < 0)
+		return -1;
+
+	err = nfnl_nft_set_alloc_cache(nf_sock, family, table, &set_cache);
+	if (err < 0) {
+		fprintf(stderr, "Unable to retrieve set cache: %s\n",
+			nl_geterror(err));
+		goto errout;
+	}
+	nl_cache_mngt_provide(set_cache);
+
+	nl_cache_dump(set_cache, &params);
+
+	err = 0;
+
+errout_cache:
+	nl_cache_free(set_cache);
+errout:
+	return err;
+}
diff --git a/src/nf-table-dump.c b/src/nf-table-dump.c
index f2d4fdc..84430ca 100644
--- a/src/nf-table-dump.c
+++ b/src/nf-table-dump.c
@@ -17,7 +17,7 @@
 static void print_usage(void)
 {
 	printf(
-	"Usage: nf-ct-dump <mode>\n"
+	"Usage: nf-table-dump <mode>\n"
 	"  mode := { brief | detailed | stats | xml }\n"
 	);
 	exit(1);



More information about the netfilter-cvslog mailing list