[nftables] payload: fix two datatypes

Patrick McHardy netfilter-cvslog-bounces at lists.netfilter.org
Tue Jul 28 14:18:35 CEST 2009


Gitweb:		http://git.netfilter.org/cgi-bin/gitweb.cgi?p=nftables.git;a=commit;h=236838e0a2b1694c72098411802f411eccba7d12
commit 236838e0a2b1694c72098411802f411eccba7d12
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:42 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:42 2009 +0200

    payload: fix two datatypes
    
    Fix typo in URG-flag and missing end-of-list marker for the arpop constants.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 11c5f88f81cb628988ddad405c5295fa237b3311
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:42 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:42 2009 +0200

    payload: add DCCP packet type definitions
    
    # nft describe dccp type
    payload expression, datatype dccp_pkttype (DCCP packet type) (basetype integer), 4 bits
    
    pre-defined symbolic constants:
    	request                       	0x0
    	response                      	0x1
    	data                          	0x2
    	ack                           	0x3
    	dataack                       	0x4
    	closereq                      	0x5
    	close                         	0x6
    	reset                         	0x7
    	sync                          	0x8
    	syncack                       	0x9
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit eaefa819182d968ba6f4958fc8d909165feee0b6
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:42 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:42 2009 +0200

    netlink: fix binop RHS byteorder
    
    The byteorder of the RHS of a binop must be set before post-processing it to
    make sure it will get byteorder-switched if necessary.
    
    Fixes invalid conntrack expression states when used with bitmasks:
    
     ct state 33554432,67108864 counter packets 1924 bytes 142960
    
     =>
    
     ct state established,related counter packets 2029 bytes 151508
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit aeb84096c8cc413f81829f07ec285c3668d795ec
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:41 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:41 2009 +0200

    netlink: dump all chains when listing rules
    
    Currently only the rules are dumped and chains are constructed based
    on the rules identities. Dump all chains manually to make sure we also
    display empty chains.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 08b4600fc361cbab55ae2f89875df7fddf7b657e
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:41 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:41 2009 +0200

    netlink: fix bitmask element reconstruction
    
    mpz_scan1() needs to begin scanning at bit 0 and the loop must accept
    bit 0 as valid. No more bits were found when ULONG_MAX is returned.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 7c1ccf819bd3a77518f4fe8970355eb78bd5ee59
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:41 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:41 2009 +0200

    debug: allow runtime control of debugging output
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit ebfd6822498965cdb9961ec1a986f0463de5c9c0
Author:     Patrick McHardy <kaber at trash.net>
AuthorDate: Tue Jul 28 14:17:35 2009 +0200
Commit:     Patrick McHardy <kaber at trash.net>
CommitDate: Tue Jul 28 14:17:35 2009 +0200

    add support for new set API and standalone sets
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>
       via  236838e0a2b1694c72098411802f411eccba7d12 (commit)
       via  11c5f88f81cb628988ddad405c5295fa237b3311 (commit)
       via  eaefa819182d968ba6f4958fc8d909165feee0b6 (commit)
       via  aeb84096c8cc413f81829f07ec285c3668d795ec (commit)
       via  08b4600fc361cbab55ae2f89875df7fddf7b657e (commit)
       via  7c1ccf819bd3a77518f4fe8970355eb78bd5ee59 (commit)
       via  ebfd6822498965cdb9961ec1a986f0463de5c9c0 (commit)
      from  414fa58ae9f283c35c8510fc31f28ba77bb5fdf5 (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 236838e0a2b1694c72098411802f411eccba7d12
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:42 2009 +0200

    payload: fix two datatypes
    
    Fix typo in URG-flag and missing end-of-list marker for the arpop constants.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 11c5f88f81cb628988ddad405c5295fa237b3311
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:42 2009 +0200

    payload: add DCCP packet type definitions
    
    # nft describe dccp type
    payload expression, datatype dccp_pkttype (DCCP packet type) (basetype integer), 4 bits
    
    pre-defined symbolic constants:
    	request                       	0x0
    	response                      	0x1
    	data                          	0x2
    	ack                           	0x3
    	dataack                       	0x4
    	closereq                      	0x5
    	close                         	0x6
    	reset                         	0x7
    	sync                          	0x8
    	syncack                       	0x9
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit eaefa819182d968ba6f4958fc8d909165feee0b6
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:42 2009 +0200

    netlink: fix binop RHS byteorder
    
    The byteorder of the RHS of a binop must be set before post-processing it to
    make sure it will get byteorder-switched if necessary.
    
    Fixes invalid conntrack expression states when used with bitmasks:
    
     ct state 33554432,67108864 counter packets 1924 bytes 142960
    
     =>
    
     ct state established,related counter packets 2029 bytes 151508
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit aeb84096c8cc413f81829f07ec285c3668d795ec
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:41 2009 +0200

    netlink: dump all chains when listing rules
    
    Currently only the rules are dumped and chains are constructed based
    on the rules identities. Dump all chains manually to make sure we also
    display empty chains.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 08b4600fc361cbab55ae2f89875df7fddf7b657e
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:41 2009 +0200

    netlink: fix bitmask element reconstruction
    
    mpz_scan1() needs to begin scanning at bit 0 and the loop must accept
    bit 0 as valid. No more bits were found when ULONG_MAX is returned.
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit 7c1ccf819bd3a77518f4fe8970355eb78bd5ee59
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:41 2009 +0200

    debug: allow runtime control of debugging output
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

commit ebfd6822498965cdb9961ec1a986f0463de5c9c0
Author: Patrick McHardy <kaber at trash.net>
Date:   Tue Jul 28 14:17:35 2009 +0200

    add support for new set API and standalone sets
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

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

 files/examples/sets_and_maps        |   53 +++++
 include/datatype.h                  |    2 +
 include/expression.h                |   20 ++-
 include/headers.h                   |   14 ++
 include/linux/netfilter/nf_tables.h |  374 ++++++++++++++++++++++++++++++-----
 include/netlink.h                   |   16 ++
 include/nftables.h                  |    5 +
 include/payload.h                   |    1 +
 include/rule.h                      |   67 ++++++-
 src/evaluate.c                      |  284 +++++++++++++++++++++++----
 src/expression.c                    |   42 ++++-
 src/main.c                          |   19 ++
 src/netlink.c                       |  374 ++++++++++++++++++++++++++++++++++-
 src/netlink_delinearize.c           |  106 +++--------
 src/netlink_linearize.c             |   72 +------
 src/parser.y                        |  292 ++++++++++++++++++---------
 src/payload.c                       |   32 +++-
 src/rule.c                          |  181 ++++++++++++++++--
 src/scanner.l                       |    6 +-
 19 files changed, 1595 insertions(+), 365 deletions(-)
 create mode 100755 files/examples/sets_and_maps
Signed-off-by: Patrick McHardy <kaber at trash.net>

diff --git a/files/examples/sets_and_maps b/files/examples/sets_and_maps
new file mode 100755
index 0000000..8dfe9f8
--- /dev/null
+++ b/files/examples/sets_and_maps
@@ -0,0 +1,53 @@
+#! /sbin/nft -nf
+#
+# Examples of set and map usage
+#
+
+# symbolic anonymous set definition built from symbolic singleton definitions
+define int_if1	 = eth0
+define int_if2	 = eth1
+define int_ifs	 = { $int_if1, $int_if2 }
+
+define ext_if1	 = eth2
+define ext_if2	 = eth3
+define ext_ifs	 = { $ext_if1, $ext_if2 }
+
+# recursive symbolic anonymous set definition
+define local_ifs = { $int_ifs, $ext_ifs }
+
+# symbolic anonymous set definition
+define tcp_ports = { ssh, domain, https, 123-125 }
+
+delete table filter
+table filter {
+	# named set of type ifindex
+	set local_ifs {
+		type ifindex
+	}
+
+	# named map of type ifindex => ipv4_address
+	map nat_map {
+		type ifindex => ipv4_address
+	}
+
+	map jump_map {
+		type ifindex => verdict
+	}
+
+	chain input_1 { counter; }
+	chain input_2 { counter; }
+	chain input {
+		hook NF_INET_LOCAL_IN 0
+
+		# symbolic anonymous sets
+		meta iif $local_ifs tcp dport $tcp_ports counter
+
+		# literal anonymous set
+		meta iif { eth0, eth1 } counter
+
+		meta iif @local_ifs counter
+		meta iif vmap @jump_map
+
+		#meta iif vmap { eth0 => jump input1, eth1 => jump input2 }
+	}
+}
diff --git a/include/expression.h b/include/expression.h
index 471033e..8804284 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -26,6 +26,7 @@
  * @EXPR_CONCAT:	concatenation
  * @EXPR_LIST:		list of expressions
  * @EXPR_SET:		literal set
+ * @EXPR_SET_REF:	set reference
  * @EXPR_MAPPING:	a single mapping (key => value)
  * @EXPR_MAP:		map operation (expr map { EXPR_MAPPING, ... })
  * @EXPR_UNARY:		byteorder conversion, generated during evaluation
@@ -46,6 +47,7 @@ enum expr_types {
 	EXPR_CONCAT,
 	EXPR_LIST,
 	EXPR_SET,
+	EXPR_SET_REF,
 	EXPR_MAPPING,
 	EXPR_MAP,
 	EXPR_UNARY,
@@ -82,6 +84,12 @@ enum ops {
 
 extern const char *expr_op_symbols[];
 
+enum symbol_types {
+	SYMBOL_VALUE,
+	SYMBOL_DEFINE,
+	SYMBOL_SET,
+};
+
 /**
  * struct expr_ctx - type context for symbol parsing during evaluation
  *
@@ -129,14 +137,12 @@ struct expr_ops {
  * @EXPR_F_CONSTANT:		constant expression
  * @EXPR_F_SINGLETON:		singleton (implies primary and constant)
  * @EXPR_F_INTERVAL_END:	set member ends an open interval
- * @SET_F_INTERVAL:		set includes ranges and/or prefix expressions
  */
 enum expr_flags {
 	EXPR_F_PRIMARY		= 0x1,
 	EXPR_F_CONSTANT		= 0x2,
 	EXPR_F_SINGLETON	= 0x4,
 	EXPR_F_INTERVAL_END	= 0x8,
-	SET_F_INTERVAL		= 0x10,
 };
 
 #include <payload.h>
@@ -176,6 +182,7 @@ struct expr {
 			/* EXPR_SYMBOL */
 			const struct scope	*scope;
 			const char		*identifier;
+			enum symbol_types	symtype;
 		};
 		struct {
 			/* EXPR_VERDICT */
@@ -195,6 +202,11 @@ struct expr {
 			/* EXPR_CONCAT, EXPR_LIST, EXPR_SET */
 			struct list_head	expressions;
 			unsigned int		size;
+			uint32_t		set_flags;
+		};
+		struct {
+			/* EXPR_SET_REF */
+			struct set		*set;
 		};
 		struct {
 			/* EXPR_UNARY */
@@ -285,6 +297,7 @@ extern struct expr *verdict_expr_alloc(const struct location *loc,
 				       int verdict, const char *chain);
 
 extern struct expr *symbol_expr_alloc(const struct location *loc,
+				      enum symbol_types type, struct scope *scope,
 				      const char *identifier);
 
 static inline void symbol_expr_set_type(struct expr *expr,
@@ -324,4 +337,7 @@ extern struct expr *mapping_expr_alloc(const struct location *loc,
 extern struct expr *map_expr_alloc(const struct location *loc,
 				   struct expr *arg, struct expr *list);
 
+extern struct expr *set_ref_expr_alloc(const struct location *loc,
+				       struct set *set);
+
 #endif /* NFTABLES_EXPRESSION_H */
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.h b/include/netlink.h
index ec9a614..22ef489 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -16,10 +16,12 @@
  *
  * @msgs:	message queue
  * @list:	list of parsed rules/chains/tables
+ * @set:	current set
  */
 struct netlink_ctx {
 	struct list_head	*msgs;
 	struct list_head	list;
+	struct set		*set;
 };
 
 extern void netlink_dump_object(struct nl_object *obj);
@@ -28,6 +30,7 @@ extern struct nfnl_nft_table *alloc_nft_table(const struct handle *h);
 extern struct nfnl_nft_chain *alloc_nft_chain(const struct handle *h);
 extern struct nfnl_nft_rule *alloc_nft_rule(const struct handle *h);
 extern struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *));
+extern struct nfnl_nft_set *alloc_nft_set(const struct handle *h);
 extern struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len);
 
 extern struct nfnl_nft_data *netlink_gen_data(const struct expr *expr);
@@ -68,4 +71,17 @@ extern int netlink_get_table(struct netlink_ctx *ctx, const struct handle *h);
 extern int netlink_list_table(struct netlink_ctx *ctx, const struct handle *h);
 extern int netlink_flush_table(struct netlink_ctx *ctx, const struct handle *h);
 
+extern int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h,
+			   struct set *set);
+extern int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_sets(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_get_set(struct netlink_ctx *ctx, const struct handle *h);
+
+extern int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
+				const struct expr *expr);
+extern int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h,
+				   const struct expr *expr);
+extern int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
+				struct set *set);
+
 #endif /* NFTABLES_NETLINK_H */
diff --git a/include/rule.h b/include/rule.h
index 01eeb21..9754307 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -6,17 +6,19 @@
 #include <list.h>
 
 /**
- * struct handle - handle for tables, chains and rules
+ * struct handle - handle for tables, chains, rules and sets
  *
  * @family:	protocol family
  * @table:	table name
  * @chain:	chain name (chains and rules only)
+ * @set:	set name (sets only)
  * @handle:	rule handle (rules only)
  */
 struct handle {
 	int			family;
 	const char		*table;
 	const char		*chain;
+	const char		*set;
 	uint32_t		handle;
 };
 
@@ -61,12 +63,14 @@ extern struct symbol *symbol_lookup(const struct scope *scope,
  * @list:	list node
  * @handle:	table handle
  * @chains:	chains contained in the table
+ * @sets:	sets contained in the table
  */
 struct table {
 	struct list_head	list;
 	struct handle		handle;
 	struct scope		scope;
 	struct list_head	chains;
+	struct list_head	sets;
 };
 
 extern struct table *table_alloc(void);
@@ -121,6 +125,53 @@ extern void rule_free(struct rule *rule);
 extern void rule_print(const struct rule *rule);
 
 /**
+ * enum set_flags
+ *
+ * @SET_F_CONSTANT:		Set content is constant
+ * @SET_F_INTERVAL:		set includes ranges and/or prefix expressions
+ */
+enum set_flags {
+	SET_F_ANONYMOUS		= 0x1,
+	SET_F_CONSTANT		= 0x2,
+	SET_F_INTERVAL		= 0x4,
+	SET_F_MAP		= 0x8,
+};
+
+/**
+ * struct set - nftables set
+ *
+ * @list:	table set list node
+ * @handle:	set handle
+ * @location:	location the set was defined/declared at
+ * @refcnt:	reference count
+ * @flags:	bitmask of set flags
+ * @keytype:	key data type
+ * @keylen:	key length
+ * @datatype:	mapping data type
+ * @datalen:	mapping data len
+ * @init:	initializer
+ */
+struct set {
+	struct list_head	list;
+	struct handle		handle;
+	struct location		location;
+	unsigned int		refcnt;
+	uint32_t		flags;
+	const struct datatype	*keytype;
+	unsigned int		keylen;
+	const struct datatype	*datatype;
+	unsigned int		datalen;
+	struct expr		*init;
+};
+
+extern struct set *set_alloc(const struct location *loc);
+extern struct set *set_get(struct set *set);
+extern void set_free(struct set *set);
+extern void set_add_hash(struct set *set, struct table *table);
+extern struct set *set_lookup(const struct table *table, const char *name);
+extern void set_print(const struct set *set);
+
+/**
  * enum cmd_ops - command operations
  *
  * @CMD_INVALID:	invalid
@@ -141,12 +192,18 @@ enum cmd_ops {
  * enum cmd_obj - command objects
  *
  * @CMD_OBJ_INVALID:	invalid
+ * @CMD_OBJ_SETELEM:	set element(s)
+ * @CMD_OBJ_SET:	set
+ * @CMD_OBJ_SETS:	multiple sets
  * @CMD_OBJ_RULE:	rule
  * @CMD_OBJ_CHAIN:	chain
  * @CMD_OBJ_TABLE:	table
  */
 enum cmd_obj {
 	CMD_OBJ_INVALID,
+	CMD_OBJ_SETELEM,
+	CMD_OBJ_SET,
+	CMD_OBJ_SETS,
 	CMD_OBJ_RULE,
 	CMD_OBJ_CHAIN,
 	CMD_OBJ_TABLE,
@@ -170,6 +227,8 @@ struct cmd {
 	struct handle		handle;
 	union {
 		void		*data;
+		struct expr	*expr;
+		struct set	*set;
 		struct rule	*rule;
 		struct chain	*chain;
 		struct table	*table;
@@ -187,12 +246,18 @@ extern void cmd_free(struct cmd *cmd);
  * struct eval_ctx - evaluation context
  *
  * @msgs:	message queue
+ * @cmd:	current command
+ * @table:	current table
+ * @set:	current set
  * @stmt:	current statement
  * @ectx:	expression context
  * @pctx:	payload context
  */
 struct eval_ctx {
 	struct list_head	*msgs;
+	struct cmd		*cmd;
+	struct table		*table;
+	struct set		*set;
 	struct stmt		*stmt;
 	struct expr_ctx		ectx;
 	struct payload_ctx	pctx;
diff --git a/src/evaluate.c b/src/evaluate.c
index ed3c688..f372377 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -52,6 +52,78 @@ static int __fmtstring(4, 5) stmt_binary_error(struct eval_ctx *ctx,
 	return -1;
 }
 
+static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,
+				       const struct set *set,
+				       const char *fmt, ...)
+{
+	struct error_record *erec;
+	va_list ap;
+
+	va_start(ap, fmt);
+	erec = erec_vcreate(EREC_ERROR, &set->location, fmt, ap);
+	va_end(ap);
+	erec_queue(erec, ctx->msgs);
+	return -1;
+}
+
+static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
+					     const struct datatype *keytype,
+					     unsigned int keylen,
+					     struct expr *expr)
+{
+	struct cmd *cmd;
+	struct set *set;
+
+	set = set_alloc(&expr->location);
+	set->flags	= SET_F_CONSTANT | SET_F_ANONYMOUS | expr->set_flags;
+	set->handle.set = xstrdup(set->flags & SET_F_MAP ? "map%d" : "set%d");
+	set->keytype 	= keytype;
+	set->keylen	= keylen;
+	set->init	= expr;
+
+	if (ctx->table != NULL)
+		list_add_tail(&set->list, &ctx->table->sets);
+	else {
+		handle_merge(&set->handle, &ctx->cmd->handle);
+		cmd = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &set->handle, set);
+		cmd->location = set->location;
+		list_add_tail(&cmd->list, &ctx->cmd->list);
+	}
+
+	return set_ref_expr_alloc(&expr->location, set);
+}
+
+// FIXME
+#include <netlink.h>
+static struct set *get_set(struct eval_ctx *ctx, const struct handle *h,
+			   const char *identifier)
+{
+	struct netlink_ctx nctx = {
+		.msgs = ctx->msgs,
+	};
+	struct handle handle;
+	struct set *set;
+	int err;
+
+	if (ctx->table != NULL) {
+		set = set_lookup(ctx->table, identifier);
+		if (set != NULL)
+			return set;
+	}
+
+	init_list_head(&nctx.list);
+
+	memset(&handle, 0, sizeof(handle));
+	handle_merge(&handle, h);
+	handle.set = xstrdup(identifier);
+	err = netlink_get_set(&nctx, &handle);
+	handle_free(&handle);
+
+	if (err < 0)
+		return NULL;
+	return list_first_entry(&nctx.list, struct set, list);
+}
+
 static enum ops byteorder_conversion_op(struct expr *expr,
 					enum byteorder byteorder)
 {
@@ -103,23 +175,32 @@ static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
 {
 	struct error_record *erec;
 	struct symbol *sym;
+	struct set *set;
 	struct expr *new;
 
-	(*expr)->dtype = ctx->ectx.dtype;
-
-	if ((*expr)->scope != NULL) {
+	switch ((*expr)->symtype) {
+	case SYMBOL_VALUE:
+		(*expr)->dtype = ctx->ectx.dtype;
+		erec = symbol_parse(*expr, &new);
+		if (erec != NULL) {
+			erec_queue(erec, ctx->msgs);
+			return -1;
+		}
+		break;
+	case SYMBOL_DEFINE:
 		sym = symbol_lookup((*expr)->scope, (*expr)->identifier);
 		if (sym == NULL)
 			return expr_error(ctx, *expr,
 					  "undefined identifier '%s'",
 					  (*expr)->identifier);
 		new = expr_clone(sym->expr);
-	} else {
-		erec = symbol_parse(*expr, &new);
-		if (erec != NULL) {
-			erec_queue(erec, ctx->msgs);
+		break;
+	case SYMBOL_SET:
+		set = get_set(ctx, &ctx->cmd->handle, (*expr)->identifier);
+		if (set == NULL)
 			return -1;
-		}
+		new = set_ref_expr_alloc(&(*expr)->location, set);
+		break;
 	}
 
 	expr_free(*expr);
@@ -548,10 +629,18 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
 	list_for_each_entry_safe(i, next, &set->expressions, list) {
 		if (list_member_evaluate(ctx, &i) < 0)
 			return -1;
+
 		if (!expr_is_constant(i))
 			return expr_error(ctx, i, "Set member is not constant");
-		if (!expr_is_singleton(i))
-			set->flags |= SET_F_INTERVAL;
+
+		if (i->ops->type == EXPR_SET) {
+			/* Merge recursive set definitions */
+			list_splice_tail_init(&i->expressions, &i->list);
+			list_del(&i->list);
+			set->set_flags |= i->set_flags;
+			expr_free(i);
+		} else if (!expr_is_singleton(i))
+			set->set_flags |= SET_F_INTERVAL;
 	}
 
 	set->dtype = ctx->ectx.dtype;
@@ -563,7 +652,7 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
 static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
 {
 	struct expr_ctx ectx = ctx->ectx;
-	struct expr *map = *expr, *i;
+	struct expr *map = *expr, *mappings;
 
 	if (expr_evaluate(ctx, &map->expr) < 0)
 		return -1;
@@ -571,39 +660,71 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
 		return expr_error(ctx, map->expr,
 				  "Map expression can not be constant");
 
-	/* FIXME: segtree needs to know the dimension of the *key*.
-	 * The len should actually be the value of the mapping. */
-	map->mappings->dtype = ctx->ectx.dtype;
-	map->mappings->len   = ctx->ectx.len;
+	mappings = map->mappings;
+	mappings->set_flags |= SET_F_MAP;
 
-	list_for_each_entry(i, &map->mappings->expressions, list) {
-		expr_set_context(&ctx->ectx, map->expr->dtype, map->expr->len);
-		if (expr_evaluate(ctx, &i->left) < 0)
+	switch (map->mappings->ops->type) {
+	case EXPR_SET:
+		mappings = implicit_set_declaration(ctx, ctx->ectx.dtype,
+						    ctx->ectx.len, mappings);
+		mappings->set->datatype = ectx.dtype;
+		mappings->set->datalen  = ectx.len;
+
+		map->mappings = mappings;
+
+		ctx->set = mappings->set;
+		if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
 			return -1;
-		if (!expr_is_constant(i->left))
-			return expr_error(ctx, i->left,
-					  "Key must be a constant");
-		if (!expr_is_singleton(i->left))
-			map->mappings->flags |= SET_F_INTERVAL;
-
-		expr_set_context(&ctx->ectx, ectx.dtype, ectx.len);
-		if (expr_evaluate(ctx, &i->right) < 0)
+		ctx->set = NULL;
+		break;
+	case EXPR_SYMBOL:
+		if (expr_evaluate(ctx, &map->mappings) < 0)
 			return -1;
-		if (!expr_is_constant(i->right))
-			return expr_error(ctx, i->right,
-					  "Mapping must be a constant");
-		if (!expr_is_singleton(i->right))
-			return expr_error(ctx, i->right,
-					  "Mapping must be a singleton");
+		if (map->mappings->ops->type != EXPR_SET_REF)
+			return expr_error(ctx, map->mappings,
+					  "Expression is not a map");
+		break;
+	default:
+		BUG();
 	}
 
 	map->dtype = ctx->ectx.dtype;
 	map->flags |= EXPR_F_CONSTANT;
 
 	/* Data for range lookups needs to be in big endian order */
-	if (map->mappings->flags & SET_F_INTERVAL &&
+	if (map->mappings->set_flags & SET_F_INTERVAL &&
 	    byteorder_conversion(ctx, &map->expr, BYTEORDER_BIG_ENDIAN) < 0)
 		return -1;
+
+	return 0;
+}
+
+static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
+{
+	struct expr *mapping = *expr;
+	struct set *set = ctx->set;
+
+	if (set == NULL)
+		return expr_error(ctx, mapping, "mapping outside of map context");
+	if (!(set->flags & SET_F_MAP))
+		return set_error(ctx, set, "set is not a map");
+
+	expr_set_context(&ctx->ectx, set->keytype, set->keylen);
+	if (expr_evaluate(ctx, &mapping->left) < 0)
+		return -1;
+	if (!expr_is_constant(mapping->left))
+		return expr_error(ctx, mapping->left, "Key must be a constant");
+	mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON;
+
+	expr_set_context(&ctx->ectx, set->datatype, set->datalen);
+	if (expr_evaluate(ctx, &mapping->right) < 0)
+		return -1;
+	if (!expr_is_constant(mapping->right))
+		return expr_error(ctx, mapping->right, "Value must be a constant");
+	if (!expr_is_singleton(mapping->right))
+		return expr_error(ctx, mapping->right, "Value must be a singleton");
+
+	mapping->flags |= EXPR_F_CONSTANT;
 	return 0;
 }
 
@@ -719,6 +840,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
 			rel->op = OP_RANGE;
 			break;
 		case EXPR_SET:
+		case EXPR_SET_REF:
 			rel->op = OP_LOOKUP;
 			break;
 		case EXPR_LIST:
@@ -732,10 +854,21 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
 
 	switch (rel->op) {
 	case OP_LOOKUP:
+		/* A literal set expression implicitly declares the set */
+		if (right->ops->type == EXPR_SET)
+			right = rel->right =
+				implicit_set_declaration(ctx, left->dtype, left->len, right);
+		else if (left->dtype != right->dtype)
+			return expr_binary_error(ctx, right, left,
+						 "datatype mismatch, expected %s, "
+						 "set has type %s",
+						 left->dtype->desc,
+						 right->dtype->desc);
+
 		/* Data for range lookups needs to be in big endian order */
-		if (right->flags & SET_F_INTERVAL &&
+		if (right->set->flags & SET_F_INTERVAL &&
 		    byteorder_conversion(ctx, &rel->left,
-			    		 BYTEORDER_BIG_ENDIAN) < 0)
+					 BYTEORDER_BIG_ENDIAN) < 0)
 			return -1;
 		left = rel->left;
 		break;
@@ -839,6 +972,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 	switch ((*expr)->ops->type) {
 	case EXPR_SYMBOL:
 		return expr_evaluate_symbol(ctx, expr);
+	case EXPR_SET_REF:
+		return 0;
 	case EXPR_VALUE:
 		return expr_evaluate_value(ctx, expr);
 	case EXPR_VERDICT:
@@ -864,6 +999,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 		return expr_evaluate_set(ctx, expr);
 	case EXPR_MAP:
 		return expr_evaluate_map(ctx, expr);
+	case EXPR_MAPPING:
+		return expr_evaluate_mapping(ctx, expr);
 	case EXPR_RELATIONAL:
 		return expr_evaluate_relational(ctx, expr);
 	default:
@@ -879,6 +1016,7 @@ static int stmt_evaluate_expr(struct eval_ctx *ctx, struct stmt *stmt)
 
 static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
 {
+	expr_set_context(&ctx->ectx, &verdict_type, 0);
 	if (expr_evaluate(ctx, &stmt->expr) < 0)
 		return -1;
 
@@ -962,6 +1100,52 @@ static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 	}
 }
 
+static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)
+{
+	struct set *set;
+
+	set = get_set(ctx, &ctx->cmd->handle, ctx->cmd->handle.set);
+	if (set == NULL)
+		return -1;
+
+	ctx->set = set;
+	expr_set_context(&ctx->ectx, set->keytype, set->keylen);
+	if (expr_evaluate(ctx, expr) < 0)
+		return -1;
+	ctx->set = NULL;
+	return 0;
+}
+
+static int set_evaluate(struct eval_ctx *ctx, struct set *set)
+{
+	const char *type;
+
+	type = set->flags & SET_F_MAP ? "map" : "set";
+
+	if (set->keytype == NULL)
+		return set_error(ctx, set, "%s definition does not specify "
+				 "key data type", type);
+
+	set->keylen = set->keytype->size;
+	if (set->keylen == 0)
+		return set_error(ctx, set, "unqualified key data type "
+				 "specified in %s definition", type);
+
+	if (!(set->flags & SET_F_MAP))
+		return 0;
+
+	if (set->datatype == NULL)
+		return set_error(ctx, set, "map definition does not specify "
+				 "mapping data type");
+
+	set->datalen = set->datatype->size;
+	if (set->datalen == 0 && set->datatype->type != TYPE_VERDICT)
+		return set_error(ctx, set, "unqualified mapping data type "
+				 "specified in map definition");
+
+	return 0;
+}
+
 static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
 {
 	struct stmt *stmt, *tstmt = NULL;
@@ -999,18 +1183,31 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
 static int table_evaluate(struct eval_ctx *ctx, struct table *table)
 {
 	struct chain *chain;
+	struct set *set;
 
+	ctx->table = table;
+	list_for_each_entry(set, &table->sets, list) {
+		handle_merge(&set->handle, &table->handle);
+		if (set_evaluate(ctx, set) < 0)
+			return -1;
+	}
 	list_for_each_entry(chain, &table->chains, list) {
 		handle_merge(&chain->handle, &table->handle);
 		if (chain_evaluate(ctx, chain) < 0)
 			return -1;
 	}
+	ctx->table = NULL;
 	return 0;
 }
 
 static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
 {
 	switch (cmd->obj) {
+	case CMD_OBJ_SETELEM:
+		return setelem_evaluate(ctx, &cmd->expr);
+	case CMD_OBJ_SET:
+		handle_merge(&cmd->set->handle, &cmd->handle);
+		return set_evaluate(ctx, cmd->set);
 	case CMD_OBJ_RULE:
 		handle_merge(&cmd->rule->handle, &cmd->handle);
 		return rule_evaluate(ctx, cmd->rule);
@@ -1027,6 +1224,21 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
 	}
 }
 
+static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
+{
+	switch (cmd->obj) {
+	case CMD_OBJ_SETELEM:
+		return setelem_evaluate(ctx, &cmd->expr);
+	case CMD_OBJ_SET:
+	case CMD_OBJ_RULE:
+	case CMD_OBJ_CHAIN:
+	case CMD_OBJ_TABLE:
+		return 0;
+	default:
+		BUG();
+	}
+}
+
 static int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
 {
 #if TRACE
@@ -1035,10 +1247,12 @@ static int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
 	erec_print(stdout, erec); printf("\n\n");
 #endif
 
+	ctx->cmd = cmd;
 	switch (cmd->op) {
 	case CMD_ADD:
 		return cmd_evaluate_add(ctx, cmd);
 	case CMD_DELETE:
+		return cmd_evaluate_delete(ctx, cmd);
 	case CMD_LIST:
 	case CMD_FLUSH:
 		return 0;
diff --git a/src/expression.c b/src/expression.c
index d854eb2..dffe7a8 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -188,6 +188,7 @@ static void symbol_expr_print(const struct expr *expr)
 
 static void symbol_expr_clone(struct expr *new, const struct expr *expr)
 {
+	new->symtype	= expr->symtype;
 	new->scope      = expr->scope;
 	new->identifier = xstrdup(expr->identifier);
 }
@@ -206,12 +207,15 @@ static const struct expr_ops symbol_expr_ops = {
 };
 
 struct expr *symbol_expr_alloc(const struct location *loc,
+			       enum symbol_types type, struct scope *scope,
 			       const char *identifier)
 {
 	struct expr *expr;
 
 	expr = expr_alloc(loc, &symbol_expr_ops, &invalid_type,
 			  BYTEORDER_INVALID, 0);
+	expr->symtype	 = type;
+	expr->scope	 = scope;
 	expr->identifier = xstrdup(identifier);
 	return expr;
 }
@@ -702,14 +706,42 @@ static const struct expr_ops map_expr_ops = {
 };
 
 struct expr *map_expr_alloc(const struct location *loc, struct expr *arg,
-			    struct expr *list)
+			    struct expr *mappings)
 {
 	struct expr *expr;
 
-	assert(list->ops->type == EXPR_SET);
-	expr = expr_alloc(loc, &map_expr_ops, list->dtype,
-			  list->byteorder, list->len);
+	expr = expr_alloc(loc, &map_expr_ops, &invalid_type, BYTEORDER_INVALID, 0);
 	expr->expr     = arg;
-	expr->mappings = list;
+	expr->mappings = mappings;
+	return expr;
+}
+
+static void set_ref_expr_print(const struct expr *expr)
+{
+	if (expr->set->flags & SET_F_ANONYMOUS)
+		expr_print(expr->set->init);
+	else
+		printf("@%s", expr->set->handle.set);
+}
+
+static void set_ref_expr_destroy(struct expr *expr)
+{
+	set_free(expr->set);
+}
+
+static const struct expr_ops set_ref_expr_ops = {
+	.type		= EXPR_SET_REF,
+	.name		= "set reference",
+	.print		= set_ref_expr_print,
+	.destroy	= set_ref_expr_destroy,
+};
+
+struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)
+{
+	struct expr *expr;
+
+	expr = expr_alloc(loc, &set_ref_expr_ops, set->keytype, 0, 0);
+	expr->set = set_get(set);
+	expr->flags |= EXPR_F_CONSTANT;
 	return expr;
 }
diff --git a/src/netlink.c b/src/netlink.c
index 8ef1401..3798154 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -15,6 +15,8 @@
 #include <netlink/netfilter/nft_rule.h>
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft_data.h>
+#include <netlink/netfilter/nft_setelem.h>
+#include <netlink/netfilter/nft_set.h>
 #include <linux/netfilter/nf_tables.h>
 
 #include <nftables.h>
@@ -30,10 +32,13 @@ static struct nl_sock *nf_sock;
 
 static void __init netlink_open_sock(void)
 {
-	// FIXME: should be done dynamically by nft_set and based on set members
 	nlmsg_set_default_size(65536);
 	nf_sock = nl_socket_alloc();
+	if (nf_sock == NULL)
+		memory_allocation_error();
+
 	nfnl_connect(nf_sock);
+	nl_socket_set_nonblocking(nf_sock);
 }
 
 static void __exit netlink_close_sock(void)
@@ -123,6 +128,42 @@ struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *))
 	return nle;
 }
 
+struct nfnl_nft_set *alloc_nft_set(const struct handle *h)
+{
+	struct nfnl_nft_set *nls;
+
+	nls = nfnl_nft_set_alloc();
+	if (nls == NULL)
+		memory_allocation_error();
+	nfnl_nft_set_set_family(nls, h->family);
+	nfnl_nft_set_set_table(nls, h->table, strlen(h->table) + 1);
+	if (h->set != NULL)
+		nfnl_nft_set_set_name(nls, h->set, strlen(h->set) + 1);
+	return nls;
+}
+
+static struct nfnl_nft_setelem *alloc_nft_setelem(const struct expr *expr)
+{
+	struct nfnl_nft_setelem *nlse;
+
+	nlse = nfnl_nft_setelem_alloc();
+	if (nlse == NULL)
+		memory_allocation_error();
+
+	if (expr->ops->type == EXPR_VALUE || expr->flags & EXPR_F_INTERVAL_END)
+		nfnl_nft_setelem_set_key(nlse, netlink_gen_data(expr));
+	else {
+		assert(expr->ops->type == EXPR_MAPPING);
+		nfnl_nft_setelem_set_key(nlse, netlink_gen_data(expr->left));
+		nfnl_nft_setelem_set_data(nlse, netlink_gen_data(expr->right));
+	}
+
+	if (expr->flags & EXPR_F_INTERVAL_END)
+		nfnl_nft_setelem_set_flags(nlse, NFT_SET_ELEM_INTERVAL_END);
+
+	return nlse;
+}
+
 struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len)
 {
 	struct nfnl_nft_data *nld;
@@ -585,3 +626,326 @@ int netlink_flush_table(struct netlink_ctx *ctx, const struct handle *h)
 {
 	return netlink_flush_rules(ctx, h);
 }
+
+static enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype)
+{
+	switch (dtype->type) {
+	case TYPE_VERDICT:
+		return NFT_DATA_VERDICT;
+	default:
+		return dtype->type;
+	}
+}
+
+static const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
+{
+	switch (type) {
+	case NFT_DATA_VERDICT:
+		return &verdict_type;
+	default:
+		return datatype_lookup(type);
+	}
+}
+
+static void add_set_cb(struct nl_object *obj, void *arg)
+{
+	struct nfnl_nft_set *nls = (struct nfnl_nft_set *)obj;
+	struct netlink_ctx *ctx = arg;
+	struct set *set = ctx->set;
+
+#if TRACE
+	netlink_dump_object(OBJ_CAST(nls));
+#endif
+	set->handle.set = xstrdup(nfnl_nft_set_get_name(nls));
+}
+
+static int netlink_add_set_cb(struct nl_msg *msg, void *arg)
+{
+	return nl_msg_parse(msg, add_set_cb, arg);
+}
+
+int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h,
+		    struct set *set)
+{
+	struct nfnl_nft_set *nls;
+	int err;
+
+	nls = alloc_nft_set(h);
+	nfnl_nft_set_set_flags(nls, set->flags);
+	nfnl_nft_set_set_keytype(nls, dtype_map_to_kernel(set->keytype));
+	nfnl_nft_set_set_keylen(nls, set->keylen / BITS_PER_BYTE);
+	if (set->flags & NFT_SET_MAP) {
+		nfnl_nft_set_set_datatype(nls, dtype_map_to_kernel(set->datatype));
+		nfnl_nft_set_set_datalen(nls, set->datalen / BITS_PER_BYTE);
+	}
+#if TRACE
+	netlink_dump_object(OBJ_CAST(nls));
+#endif
+
+	ctx->set = set;
+	netlink_set_callback(netlink_add_set_cb, ctx);
+	err = nfnl_nft_set_add(nf_sock, nls, NLM_F_EXCL | NLM_F_ECHO);
+	if (err == 0)
+		err = nl_recvmsgs_default(nf_sock);
+	netlink_set_callback(NULL, NULL);
+	nfnl_nft_set_put(nls);
+	ctx->set = NULL;
+
+	if (err < 0)
+		netlink_io_error(ctx, NULL, "Could not add set: %s",
+				 nl_geterror(err));
+	return err;
+}
+
+int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h)
+{
+	struct nfnl_nft_set *nls;
+	int err;
+
+	nls = alloc_nft_set(h);
+	err = nfnl_nft_set_delete(nf_sock, nls, 0);
+	nfnl_nft_set_put(nls);
+
+	if (err < 0)
+		netlink_io_error(ctx, NULL, "Could not delete set: %s",
+				 nl_geterror(err));
+	return err;
+}
+
+static void list_set_cb(struct nl_object *obj, void *arg)
+{
+	struct nfnl_nft_set *nls = (struct nfnl_nft_set *)obj;
+	struct netlink_ctx *ctx = arg;
+	const struct datatype *keytype, *datatype;
+	uint32_t flags;
+	struct set *set;
+#if TRACE
+	netlink_dump_object(obj);
+#endif
+	if (!nfnl_nft_set_test_family(nls) ||
+	    !nfnl_nft_set_test_table(nls) ||
+	    !nfnl_nft_set_test_name(nls) ||
+	    !nfnl_nft_set_test_keytype(nls) ||
+	    !nfnl_nft_set_test_keylen(nls)) {
+		netlink_io_error(ctx, NULL, "Incomplete set received");
+		return;
+	}
+
+	keytype = dtype_map_from_kernel(nfnl_nft_set_get_keytype(nls));
+	if (keytype == NULL) {
+		netlink_io_error(ctx, NULL, "Unknown data type in set key %u",
+				 nfnl_nft_set_get_keytype(nls));
+		return;
+	}
+
+	flags = nfnl_nft_set_get_flags(nls);
+	if (flags & NFT_SET_MAP) {
+		datatype = dtype_map_from_kernel(nfnl_nft_set_get_datatype(nls));
+		if (datatype == NULL) {
+			netlink_io_error(ctx, NULL, "Unknown data type in set key %u",
+					 nfnl_nft_set_get_datatype(nls));
+			return;
+		}
+	} else
+		datatype = NULL;
+
+	set = set_alloc(&internal_location);
+	set->handle.family = nfnl_nft_set_get_family(nls);
+	set->handle.table  = xstrdup(nfnl_nft_set_get_table(nls));
+	set->handle.set    = xstrdup(nfnl_nft_set_get_name(nls));
+	set->keytype       = keytype;
+	set->keylen        = nfnl_nft_set_get_keylen(nls) * BITS_PER_BYTE;
+	set->flags         = flags;
+	set->datatype      = datatype;
+	set->datalen       = nfnl_nft_set_get_datalen(nls) * BITS_PER_BYTE;
+	list_add_tail(&set->list, &ctx->list);
+}
+
+int netlink_list_sets(struct netlink_ctx *ctx, const struct handle *h)
+{
+	struct nl_cache *set_cache;
+	int err;
+
+	err = nfnl_nft_set_alloc_cache(nf_sock, h->family, h->table, &set_cache);
+	if (err < 0)
+		return netlink_io_error(ctx, NULL,
+					"Could not receive sets from kernel: %s",
+					nl_geterror(err));
+
+	nl_cache_foreach(set_cache, list_set_cb, ctx);
+	nl_cache_free(set_cache);
+	return 0;
+}
+
+static int netlink_get_set_cb(struct nl_msg *msg, void *arg)
+{
+	return nl_msg_parse(msg, list_set_cb, arg);
+}
+
+int netlink_get_set(struct netlink_ctx *ctx, const struct handle *h)
+{
+	struct nfnl_nft_set *nls;
+	int err;
+
+	nls = alloc_nft_set(h);
+#if TRACE
+	netlink_dump_object(OBJ_CAST(nls));
+#endif
+	netlink_set_callback(netlink_get_set_cb, ctx);
+	err = nfnl_nft_set_query(nf_sock, nls, 0);
+	if (err == 0)
+		err = nl_recvmsgs_default(nf_sock);
+	netlink_set_callback(NULL, NULL);
+
+	nfnl_nft_set_put(nls);
+
+	if (err < 0)
+		return netlink_io_error(ctx, NULL,
+					"Could not receive set from kernel: %s",
+					nl_geterror(err));
+	return err;
+}
+
+static int alloc_setelem_cache(const struct expr *set, struct nl_cache **res)
+{
+	struct nfnl_nft_setelem *nlse;
+	struct nl_cache *elements;
+	const struct expr *expr;
+	int err;
+
+	err = nl_cache_alloc_name("netfilter/nft_setelem", &elements);
+	if (err < 0)
+		return err;
+	list_for_each_entry(expr, &set->expressions, list) {
+		nlse = alloc_nft_setelem(expr);
+		nl_cache_add(elements, OBJ_CAST(nlse));
+	}
+	*res = elements;
+	return 0;
+}
+
+int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
+			 const struct expr *expr)
+{
+	struct nfnl_nft_set *nls;
+	struct nl_cache *elements;
+	int err;
+
+	nls = alloc_nft_set(h);
+#if TRACE
+	netlink_dump_object(OBJ_CAST(nls));
+#endif
+	err = alloc_setelem_cache(expr, &elements);
+	if (err < 0)
+		goto out;
+	err = nfnl_nft_setelem_add(nf_sock, nls, elements, 0);
+	if (err < 0)
+		goto out;
+	err = nl_recvmsgs_default(nf_sock);
+out:
+	nfnl_nft_set_put(nls);
+	if (err < 0)
+		netlink_io_error(ctx, NULL, "Could not add set elements: %s",
+				 nl_geterror(err));
+	return err;
+}
+
+int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h,
+			    const struct expr *expr)
+{
+	struct nfnl_nft_set *nls;
+	struct nl_cache *elements;
+	int err;
+
+	nls = alloc_nft_set(h);
+	err = alloc_setelem_cache(expr, &elements);
+	if (err < 0)
+		goto out;
+	err = nfnl_nft_setelem_delete(nf_sock, nls, elements, 0);
+	if (err < 0)
+		goto out;
+	err = nl_recvmsgs_default(nf_sock);
+out:
+	nfnl_nft_set_put(nls);
+	if (err < 0)
+		netlink_io_error(ctx, NULL, "Could not delete set elements: %s",
+				 nl_geterror(err));
+	return err;
+}
+
+static void list_setelem_cb(struct nl_object *obj, void *arg)
+{
+	struct nfnl_nft_setelem *nlse = nl_object_priv(obj);
+	struct nfnl_nft_data *nld;
+	struct netlink_ctx *ctx = arg;
+	struct set *set = ctx->set;
+	struct expr *expr, *data;
+	uint32_t flags;
+#if TRACE
+	netlink_dump_object(obj);
+#endif
+	if (!nfnl_nft_setelem_test_key(nlse)) {
+		netlink_io_error(ctx, NULL, "Incomplete set element received");
+		return;
+	}
+
+	nld   = nfnl_nft_setelem_get_key(nlse);
+	flags = nfnl_nft_setelem_get_flags(nlse);
+
+	expr = netlink_alloc_value(&internal_location, nld);
+	expr->dtype	= set->keytype;
+	expr->byteorder	= set->keytype->byteorder;
+	if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+		mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+	if (flags & NFT_SET_ELEM_INTERVAL_END)
+		expr->flags |= EXPR_F_INTERVAL_END;
+	else if (nfnl_nft_setelem_test_data(nlse)) {
+		nld = nfnl_nft_setelem_get_data(nlse);
+
+		data = netlink_alloc_data(&internal_location, nld,
+					  set->datatype->type == EXPR_VERDICT ?
+					  NFT_REG_VERDICT : NFT_REG_1);
+		data->dtype = set->datatype;
+
+		expr = mapping_expr_alloc(&internal_location, expr, data);
+	}
+
+	compound_expr_add(set->init, expr);
+}
+
+extern void interval_map_decompose(struct expr *set);
+
+int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
+			 struct set *set)
+{
+	struct nl_cache *elements;
+	struct nfnl_nft_set *nls;
+	int err;
+
+	nls = alloc_nft_set(h);
+#if TRACE
+	netlink_dump_object(OBJ_CAST(nls));
+#endif
+	err = nfnl_nft_setelem_alloc_cache(nf_sock, nls, &elements);
+	if (err < 0)
+		goto out;
+	err = nl_recvmsgs_default(nf_sock);
+	if (err < 0)
+		goto out;
+
+	ctx->set = set;
+	set->init = set_expr_alloc(&internal_location);
+	nl_cache_foreach(elements, list_setelem_cb, ctx);
+	nl_cache_free(elements);
+	ctx->set = NULL;
+
+	if (set->flags & NFT_SET_INTERVAL)
+		interval_map_decompose(set->init);
+out:
+	nfnl_nft_set_put(nls);
+	if (err < 0)
+		netlink_io_error(ctx, NULL, "Could not receive set elements: %s",
+				 nl_geterror(err));
+	return err;
+}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 4b55939..a511313 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -19,6 +19,7 @@
 
 struct netlink_parse_ctx {
 	struct list_head	*msgs;
+	struct table		*table;
 	struct rule		*rule;
 	struct expr		*registers[NFT_REG_MAX + 1];
 };
@@ -132,91 +133,34 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
-struct netlink_data_ctx {
-	const struct location	*location;
-	struct expr		*expr;
-	enum nft_registers	dreg;
-};
-
-static void netlink_data_ctx_init(struct netlink_data_ctx *dctx,
-				  const struct location *loc,
-				  struct expr *expr, enum nft_registers dreg)
-{
-	dctx->location	= loc;
-	dctx->expr	= expr;
-	dctx->dreg	= dreg;
-}
-
-static void netlink_set_parse_data(struct nfnl_nft_data *data,
-				   enum nft_set_elem_flags flags,
-				   void *arg)
-{
-	struct netlink_data_ctx *dctx = arg;
-	struct expr *expr;
-
-	assert(dctx->dreg != NFT_REG_VERDICT);
-	expr = netlink_alloc_value(dctx->location, data);
-	if (flags & NFT_SE_INTERVAL_END)
-		expr->flags |= EXPR_F_INTERVAL_END;
-	compound_expr_add(dctx->expr, expr);
-}
-
-static void netlink_set_parse_mapping(struct nfnl_nft_data *data,
-				      struct nfnl_nft_data *mapping,
-				      enum nft_set_elem_flags flags,
-				      void *arg)
-{
-	struct netlink_data_ctx *dctx = arg;
-	struct expr *expr, *left, *right;
-
-	left  = netlink_alloc_value(dctx->location, data);
-	if (mapping != NULL) {
-		right = netlink_alloc_data(dctx->location, mapping, dctx->dreg);
-		expr  = mapping_expr_alloc(dctx->location, left, right);
-	} else
-		expr  = left;
-
-	if (flags & NFT_SE_INTERVAL_END)
-		expr->flags |= EXPR_F_INTERVAL_END;
-	compound_expr_add(dctx->expr, expr);
-}
-
-extern void interval_map_decompose(struct expr *set);
-
-static void netlink_parse_set(struct netlink_parse_ctx *ctx,
-			      const struct location *loc,
-			      const struct nfnl_nft_expr *nle)
+static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,
+				 const struct location *loc,
+				 const struct nfnl_nft_expr *nle)
 {
 	struct stmt *stmt;
 	struct expr *expr, *left, *right;
-	struct netlink_data_ctx dctx;
+	struct set *set;
 	enum nft_registers dreg;
-	enum nft_set_flags flags;
 
-	left = netlink_get_register(ctx, loc, nfnl_nft_set_get_sreg(nle));
+	left = netlink_get_register(ctx, loc, nfnl_nft_lookup_get_sreg(nle));
 	if (left == NULL)
 		return netlink_error(ctx, loc,
-				     "Set expression has no left hand side");
+				     "Lookup expression has no left hand side");
 
-	right = set_expr_alloc(loc);
+	set = set_lookup(ctx->table, nfnl_nft_lookup_get_set(nle));
+	if (set == NULL)
+		return netlink_error(ctx, loc,
+				     "Unknown set '%s' in lookup expression",
+				     nfnl_nft_lookup_get_set(nle));
 
-	flags = nfnl_nft_set_get_flags(nle);
-	if (flags & NFT_SET_MAP) {
-		dreg = nfnl_nft_set_get_dreg(nle);
-		netlink_data_ctx_init(&dctx, loc, right, dreg);
-		nfnl_nft_set_foreach_mapping(nle, netlink_set_parse_mapping,
-					     &dctx);
+	right = set_ref_expr_alloc(loc, set);
 
+	if (nfnl_nft_lookup_test_dreg(nle)) {
+		dreg = nfnl_nft_lookup_get_dreg(nle);
 		expr = map_expr_alloc(loc, left, right);
 		if (dreg != NFT_REG_VERDICT)
 			return netlink_set_register(ctx, dreg, expr);
 	} else {
-		netlink_data_ctx_init(&dctx, loc, right, EXPR_VALUE);
-		nfnl_nft_set_foreach_elem(nle, netlink_set_parse_data, &dctx);
-		if (flags & NFT_SET_INTERVAL) {
-			interval_map_decompose(right);
-			right->flags |= NFT_SET_INTERVAL;
-		}
 		expr = relational_expr_alloc(loc, OP_LOOKUP, left, right);
 	}
 
@@ -487,7 +431,7 @@ static const struct {
 } netlink_parsers[] = {
 	{ .name = "immediate",	.parse = netlink_parse_immediate },
 	{ .name = "cmp",	.parse = netlink_parse_cmp },
-	{ .name = "set",	.parse = netlink_parse_set },
+	{ .name = "lookup",	.parse = netlink_parse_lookup },
 	{ .name = "bitwise",	.parse = netlink_parse_bitwise },
 	{ .name = "byteorder",	.parse = netlink_parse_byteorder },
 	{ .name = "payload",	.parse = netlink_parse_payload },
@@ -586,12 +530,6 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
 	switch (expr->ops->type) {
 	case EXPR_MAP:
 		expr_postprocess(ctx, stmt, &expr->expr);
-		list_for_each_entry(i, &expr->mappings->expressions, list) {
-			if (i->flags & EXPR_F_INTERVAL_END)
-				continue;
-			expr_set_type(i->left, expr->expr->dtype,
-				      expr->expr->byteorder);
-		}
 		expr_postprocess(ctx, stmt, &expr->mappings);
 		break;
 	case EXPR_MAPPING:
@@ -682,6 +620,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
 			expr->len = len;
 		}
 		break;
+	case EXPR_SET_REF:
 	case EXPR_EXTHDR:
 	case EXPR_META:
 	case EXPR_CT:
@@ -734,6 +673,8 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
 	h.handle = nfnl_nft_rule_get_handle(nlr);
 
 	pctx->rule = rule_alloc(&internal_location, &h);
+	pctx->table = table_lookup(&h);
+	assert(pctx->table != NULL);
 	nfnl_nft_rule_foreach_expr(nlr, netlink_parse_expr, pctx);
 
 	rule_parse_postprocess(pctx, pctx->rule);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 65e4b69..121dca1 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -105,17 +105,10 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
 			    enum nft_registers dreg)
 {
 	struct nfnl_nft_expr *nle;
-	struct nfnl_nft_data *data;
-	struct nfnl_nft_data *mapping;
-	const struct expr *i;
-	enum nft_set_elem_flags flags;
 	enum nft_registers sreg;
-	unsigned int klen, dlen;
 
-	assert(expr->mappings->ops->type == EXPR_SET);
+	assert(expr->mappings->ops->type == EXPR_SET_REF);
 
-	klen = expr->expr->len / BITS_PER_BYTE;
-	dlen = expr->mappings->len / BITS_PER_BYTE;
 	if (dreg == NFT_REG_VERDICT)
 		sreg = get_register(ctx);
 	else
@@ -123,38 +116,10 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
 
 	netlink_gen_expr(ctx, expr->expr, sreg);
 
-	nle = alloc_nft_expr(nfnl_nft_set_init);
-	nfnl_nft_set_set_flags(nle, NFT_SET_MAP);
-	nfnl_nft_set_set_sreg(nle, sreg);
-	nfnl_nft_set_set_klen(nle, klen);
-	nfnl_nft_set_set_dreg(nle, dreg);
-	nfnl_nft_set_set_dlen(nle, dlen);
-
-	if (expr->mappings->flags & SET_F_INTERVAL) {
-		set_to_intervals(expr->mappings);
-		nfnl_nft_set_set_flags(nle, NFT_SET_INTERVAL);
-	}
-
-	list_for_each_entry(i, &expr->mappings->expressions, list) {
-		flags = 0;
-
-		switch (i->ops->type) {
-		case EXPR_MAPPING:
-			data	= netlink_gen_data(i->left);
-			mapping	= netlink_gen_data(i->right);
-			break;
-		case EXPR_VALUE:
-			assert(i->flags & EXPR_F_INTERVAL_END);
-			data    = netlink_gen_data(i);
-			mapping = NULL;
-			flags   = NFT_SE_INTERVAL_END;
-			break;
-		default:
-			BUG();
-		}
-
-		nfnl_nft_set_add_mapping(nle, data, mapping, flags);
-	}
+	nle = alloc_nft_expr(nfnl_nft_lookup_init);
+	nfnl_nft_lookup_set_sreg(nle, sreg);
+	nfnl_nft_lookup_set_dreg(nle, dreg);
+	nfnl_nft_lookup_set_set(nle, expr->mappings->set->handle.set);
 
 	if (dreg == NFT_REG_VERDICT)
 		release_register(ctx);
@@ -167,34 +132,17 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
 			       enum nft_registers dreg)
 {
 	struct nfnl_nft_expr *nle;
-	const struct expr *i;
-	enum nft_set_elem_flags flags;
 	enum nft_registers sreg;
 
-	assert(expr->right->ops->type == EXPR_SET);
+	assert(expr->right->ops->type == EXPR_SET_REF);
 	assert(dreg == NFT_REG_VERDICT);
 
 	sreg = get_register(ctx);
 	netlink_gen_expr(ctx, expr->left, sreg);
 
-	nle = alloc_nft_expr(nfnl_nft_set_init);
-	nfnl_nft_set_set_sreg(nle, sreg);
-	nfnl_nft_set_set_klen(nle, expr->left->len / BITS_PER_BYTE);
-
-	if (expr->right->flags & SET_F_INTERVAL) {
-		set_to_intervals(expr->right);
-		nfnl_nft_set_set_flags(nle, NFT_SET_INTERVAL);
-	}
-
-	list_for_each_entry(i, &expr->right->expressions, list) {
-		assert(i->ops->type == EXPR_VALUE);
-
-		flags = 0;
-		if (i->flags & EXPR_F_INTERVAL_END)
-			flags = NFT_SE_INTERVAL_END;
-
-		nfnl_nft_set_add_elem(nle, netlink_gen_data(i), flags);
-	}
+	nle = alloc_nft_expr(nfnl_nft_lookup_init);
+	nfnl_nft_lookup_set_sreg(nle, sreg);
+	nfnl_nft_lookup_set_set(nle, expr->right->set->handle.set);
 
 	release_register(ctx);
 	nfnl_nft_rule_add_expr(ctx->nlr, nle);
diff --git a/src/parser.y b/src/parser.y
index c63a14e..419dcd7 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -119,6 +119,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 	struct rule		*rule;
 	struct stmt		*stmt;
 	struct expr		*expr;
+	struct set		*set;
 }
 
 %token TOKEN_EOF 0		"end of file"
@@ -146,9 +147,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token DASH			"-"
 %token AT			"@"
 %token ARROW			"=>"
-%token MAP			"map"
 %token VMAP			"vmap"
-%token SET			"set"
 
 %token INCLUDE			"include"
 %token DEFINE			"define"
@@ -158,6 +157,10 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token TABLE			"table"
 %token CHAIN			"chain"
 %token RULE			"rule"
+%token SETS			"sets"
+%token SET			"set"
+%token ELEMENT			"element"
+%token MAP			"map"
 %token HANDLE			"handle"
 
 %token ADD			"add"
@@ -327,15 +330,23 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 
 %type <handle>			table_spec chain_spec chain_identifier ruleid_spec
 %destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec
+%type <handle>			set_spec set_identifier
+%destructor { handle_free(&$$); } set_spec set_identifier
 %type <val>			handle_spec family_spec
 
 %type <table>			table_block_alloc table_block
 %destructor { table_free($$); }	table_block_alloc
-%type <chain>			table_line chain_block_alloc chain_block
-%destructor { chain_free($$); }	table_line chain_block_alloc
+%type <chain>			chain_block_alloc chain_block
+%destructor { chain_free($$); }	chain_block_alloc
 %type <rule>			rule
 %destructor { rule_free($$); }	rule
 
+%type <set>			set_block_alloc set_block
+%destructor { set_free($$); }	set_block_alloc
+
+%type <set>			map_block_alloc map_block
+%destructor { set_free($$); }	map_block_alloc
+
 %type <list>			stmt_list
 %destructor { stmt_list_free($$); xfree($$); } stmt_list
 %type <stmt>			stmt match_stmt verdict_stmt
@@ -372,11 +383,11 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <expr>			concat_expr map_lhs_expr
 %destructor { expr_free($$); }	concat_expr map_lhs_expr
 
-%type <expr>			map_expr map_list map_list_expr
-%destructor { expr_free($$); }	map_expr map_list map_list_expr
+%type <expr>			map_expr
+%destructor { expr_free($$); }	map_expr
 
-%type <expr>			verdict_map_expr verdict_map_list verdict_map_list_expr
-%destructor { expr_free($$); }	verdict_map_expr verdict_map_list verdict_map_list_expr
+%type <expr>			verdict_map_expr
+%destructor { expr_free($$); }	verdict_map_expr
 
 %type <expr>			set_expr set_list_expr set_list_member_expr
 %destructor { expr_free($$); }	set_expr set_list_expr set_list_member_expr
@@ -384,10 +395,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <expr>			expr initializer_expr
 %destructor { expr_free($$); }	expr initializer_expr
 
-%type <expr>			match_expr
-%destructor { expr_free($$); }	match_expr
-%type <expr>			relational_expr membership_expr
-%destructor { expr_free($$); }	relational_expr membership_expr
+%type <expr>			relational_expr
+%destructor { expr_free($$); }	relational_expr
 %type <val>			relational_op
 
 %type <expr>			payload_expr payload_raw_expr
@@ -520,6 +529,24 @@ add_cmd			:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$1, $2);
 			}
+			|	SET		set_spec	set_block_alloc
+						'{'	set_block	'}'
+			{
+				$5->location = @5;
+				handle_merge(&$3->handle, &$2);
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, $5);
+			}
+			|	MAP		set_spec	map_block_alloc
+						'{'	map_block	'}'
+			{
+				$5->location = @5;
+				handle_merge(&$3->handle, &$2);
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, $5);
+			}
+			|	ELEMENT		set_spec	set_expr
+			{
+				$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEM, &$2, $3);
+			}
 			;
 
 delete_cmd		:	TABLE		table_spec
@@ -534,6 +561,14 @@ delete_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, NULL);
 			}
+			|	SET		set_spec
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, NULL);
+			}
+			|	ELEMENT		set_spec	set_expr
+			{
+				$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SETELEM, &$2, $3);
+			}
 			;
 
 list_cmd		:	TABLE		table_spec
@@ -544,6 +579,14 @@ list_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAIN, &$2, NULL);
 			}
+			|	SETS		table_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$2, NULL);
+			}
+			|	SET		set_spec
+			{
+				$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, NULL);
+			}
 			;
 
 flush_cmd		:	TABLE		table_spec
@@ -554,6 +597,10 @@ flush_cmd		:	TABLE		table_spec
 			{
 				$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_CHAIN, &$2, NULL);
 			}
+			|	SET		set_spec
+			{
+				$$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, NULL);
+			}
 			;
 
 table_block_alloc	:	/* empty */
@@ -566,20 +613,35 @@ table_block_alloc	:	/* empty */
 table_block		:	/* empty */	{ $$ = $<table>-1; }
 			|	table_block	common_block
 			|	table_block	stmt_seperator
-			|	table_block	table_line	stmt_seperator
+			|	table_block	CHAIN		chain_identifier
+					chain_block_alloc	'{' 	chain_block	'}'
+					stmt_seperator
 			{
-				list_add_tail(&$2->list, &$1->chains);
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				close_scope(state);
+				list_add_tail(&$4->list, &$1->chains);
 				$$ = $1;
 			}
-			;
-
-table_line		:	CHAIN		chain_identifier	chain_block_alloc
-	    					'{' 	chain_block	'}'
-	    		{
-				handle_merge(&$3->handle, &$2);
-				handle_free(&$2);
-				close_scope(state);
-				$$ = $3;
+			|	table_block	SET		set_identifier
+					set_block_alloc		'{'	set_block	'}'
+					stmt_seperator
+			{
+				$4->location = @3;
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				list_add_tail(&$4->list, &$1->sets);
+				$$ = $1;
+			}
+			|	table_block	MAP		set_identifier
+					map_block_alloc		'{'	map_block	'}'
+					stmt_seperator
+			{
+				$4->location = @3;
+				handle_merge(&$4->handle, &$3);
+				handle_free(&$3);
+				list_add_tail(&$4->list, &$1->sets);
+				$$ = $1;
 			}
 			;
 
@@ -601,6 +663,59 @@ chain_block		:	/* empty */	{ $$ = $<chain>-1; }
 			}
 			;
 
+set_block_alloc		:	/* empty */
+			{
+				$$ = set_alloc(NULL);
+			}
+			;
+
+set_block		:	/* empty */	{ $$ = $<set>-1; }
+			|	set_block	common_block
+			|	set_block	stmt_seperator
+			|	set_block	TYPE		identifier	stmt_seperator
+			{
+				$1->keytype = datatype_lookup_byname($3);
+				if ($1->keytype == NULL) {
+					erec_queue(error(&@3, "unknown datatype %s", $3),
+						   state->msgs);
+					YYERROR;
+				}
+				$$ = $1;
+			}
+			;
+
+map_block_alloc		:	/* empty */
+			{
+				$$ = set_alloc(NULL);
+				$$->flags |= NFT_SET_MAP;
+			}
+			;
+
+map_block		:	/* empty */	{ $$ = $<set>-1; }
+			|	map_block	common_block
+			|	map_block	stmt_seperator
+			|	map_block	TYPE
+						identifier	ARROW	identifier
+						stmt_seperator
+			{
+				$1->keytype = datatype_lookup_byname($3);
+				if ($1->keytype == NULL) {
+					erec_queue(error(&@3, "unknown datatype %s", $3),
+						   state->msgs);
+					YYERROR;
+				}
+
+				$1->datatype = datatype_lookup_byname($5);
+				if ($1->datatype == NULL) {
+					erec_queue(error(&@5, "unknown datatype %s", $5),
+						   state->msgs);
+					YYERROR;
+				}
+
+				$$ = $1;
+			}
+			;
+
 hook_spec		:	HOOK		HOOKNUM		NUM
 			{
 				$<chain>0->hooknum	= $2;
@@ -649,6 +764,20 @@ chain_identifier	:	identifier
 			}
 			;
 
+set_spec		:	table_spec	identifier
+			{
+				$$		= $1;
+				$$.set		= $2;
+			}
+			;
+
+set_identifier		:	identifier
+			{
+				memset(&$$, 0, sizeof($$));
+				$$.set		= $1;
+			}
+			;
+
 handle_spec		:	/* empty */
 			{
 				$$ = 0;
@@ -806,7 +935,7 @@ nat_stmt_args		:	expr
 			}
 			;
 
-match_stmt		:	match_expr
+match_stmt		:	relational_expr
 			{
 				$$ = expr_stmt_alloc(&@$, $1);
 			}
@@ -814,13 +943,23 @@ match_stmt		:	match_expr
 
 symbol_expr		:	string
 			{
-				$$ = symbol_expr_alloc(&@$, $1);
+				$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+						       current_scope(state),
+						       $1);
 				xfree($1);
 			}
 			|	'$'	identifier
 			{
-				$$ = symbol_expr_alloc(&@$, $2);
-				$$->scope = current_scope(state);
+				$$ = symbol_expr_alloc(&@$, SYMBOL_DEFINE,
+						       current_scope(state),
+						       $2);
+				xfree($2);
+			}
+			|	AT	identifier
+			{
+				$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
+						       current_scope(state),
+						       $2);
 				xfree($2);
 			}
 			;
@@ -830,7 +969,9 @@ integer_expr		:	NUM
 				char str[64];
 
 				snprintf(str, sizeof(str), "%" PRIu64, $1);
-				$$ = symbol_expr_alloc(&@$, str);
+				$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+						       current_scope(state),
+						       str);
 			}
 			;
 
@@ -938,76 +1079,62 @@ map_lhs_expr		:	multiton_expr
 			|	concat_expr
 			;
 
-map_expr		:	concat_expr	MAP	'{'	map_list '}'
+map_expr		:	concat_expr	MAP	expr
 			{
-				$$ = map_expr_alloc(&@$, $1, $4);
+				$$ = map_expr_alloc(&@$, $1, $3);
 			}
 			;
 
-map_list		:	map_list_expr
+verdict_map_expr	:	concat_expr	VMAP	expr
 			{
-				$$ = set_expr_alloc(&@$);
-				compound_expr_add($$, $1);
+				$$ = map_expr_alloc(&@$, $1, $3);
 			}
-			|	map_list	COMMA	map_list_expr
-			{
-				compound_expr_add($1, $3);
-				$1->location = @$;
-				$$ = $1;
-			}
-			|	map_list	COMMA	opt_newline
 			;
 
-map_list_expr		:	opt_newline	map_lhs_expr	opt_newline
-	       					ARROW		opt_newline
-						concat_expr	opt_newline
-			{
-				$$ = mapping_expr_alloc(&@$, $2, $6);
-			}
+expr			:	concat_expr
+			|	set_expr
+			|       map_expr
+			|	multiton_expr
 			;
 
-verdict_map_expr	:	concat_expr	VMAP	'{'	verdict_map_list '}'
+set_expr		:	'{'	set_list_expr		'}'
 			{
-				$$ = map_expr_alloc(&@$, $1, $4);
+				$2->location = @$;
+				$$ = $2;
 			}
 			;
 
-verdict_map_list	:	verdict_map_list_expr
+set_list_expr		:	set_list_member_expr
 			{
 				$$ = set_expr_alloc(&@$);
 				compound_expr_add($$, $1);
 			}
-			|	verdict_map_list	COMMA	verdict_map_list_expr
+			|	set_list_expr		COMMA	set_list_member_expr
 			{
 				compound_expr_add($1, $3);
-				$1->location = @$;
 				$$ = $1;
 			}
-			|	verdict_map_list	COMMA	opt_newline
+			|	set_list_expr		COMMA	opt_newline
 			;
 
-verdict_map_list_expr	:	opt_newline	map_lhs_expr	opt_newline
-		      				ARROW		opt_newline
-						verdict_expr	opt_newline
+set_list_member_expr	:	opt_newline	expr	opt_newline
 			{
-				$$ = mapping_expr_alloc(&@$, $2, $6);
+				$$ = $2;
+			}
+			|	opt_newline	map_lhs_expr	ARROW	concat_expr	opt_newline
+			{
+				$$ = mapping_expr_alloc(&@$, $2, $4);
+			}
+			|	opt_newline	map_lhs_expr	ARROW	verdict_expr	opt_newline
+			{
+				$$ = mapping_expr_alloc(&@$, $2, $4);
 			}
-			;
-
-expr			:	concat_expr
-			|       map_expr
-			|	multiton_expr
 			;
 
 initializer_expr	:	expr
-			|	set_expr
 			|	list_expr
 			;
 
-match_expr		:	relational_expr
-			|	membership_expr
-			;
-
 relational_expr		:	expr	/* implicit */	expr
 			{
 				$$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
@@ -1030,38 +1157,6 @@ relational_op		:	EQ		{ $$ = OP_EQ; }
 			|	LTE		{ $$ = OP_LTE; }
 			;
 
-membership_expr		:	expr	set_expr
-			{
-				$$ = relational_expr_alloc(&@$, OP_LOOKUP, $1, $2);
-			}
-			;
-
-set_expr		:	'{'	set_list_expr		'}'
-	  		{
-				$2->location = @$;
-				$$ = $2;
-			}
-			;
-
-set_list_expr		:	set_list_member_expr
-			{
-				$$ = set_expr_alloc(&@$);
-				compound_expr_add($$, $1);
-			}
-			|	set_list_expr		COMMA	set_list_member_expr
-			{
-				compound_expr_add($1, $3);
-				$$ = $1;
-			}
-			|	set_list_expr		COMMA	opt_newline
-			;
-
-set_list_member_expr	:	opt_newline	expr	opt_newline
-			{
-				$$ = $2;
-			}
-			;
-
 verdict_expr		:	ACCEPT
 			{
 				$$ = verdict_expr_alloc(&@$, NF_ACCEPT, NULL);
diff --git a/src/rule.c b/src/rule.c
index 6f322b8..a5032cf 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -23,6 +23,7 @@ void handle_free(struct handle *h)
 {
 	xfree(h->table);
 	xfree(h->chain);
+	xfree(h->set);
 }
 
 void handle_merge(struct handle *dst, const struct handle *src)
@@ -33,10 +34,80 @@ void handle_merge(struct handle *dst, const struct handle *src)
 		dst->table = xstrdup(src->table);
 	if (dst->chain == NULL && src->chain != NULL)
 		dst->chain = xstrdup(src->chain);
+	if (dst->set == NULL && src->set != NULL)
+		dst->set = xstrdup(src->set);
 	if (dst->handle == 0)
 		dst->handle = src->handle;
 }
 
+struct set *set_alloc(const struct location *loc)
+{
+	struct set *set;
+
+	set = xzalloc(sizeof(*set));
+	set->refcnt = 1;
+	if (loc != NULL)
+		set->location = *loc;
+	return set;
+}
+
+struct set *set_get(struct set *set)
+{
+	set->refcnt++;
+	return set;
+}
+
+void set_free(struct set *set)
+{
+	if (--set->refcnt > 0)
+		return;
+	handle_free(&set->handle);
+	xfree(set);
+}
+
+void set_add_hash(struct set *set, struct table *table)
+{
+	list_add_tail(&set->list, &table->sets);
+}
+
+struct set *set_lookup(const struct table *table, const char *name)
+{
+	struct set *set;
+
+	list_for_each_entry(set, &table->sets, list) {
+		if (!strcmp(set->handle.set, name))
+			return set;
+	}
+	return NULL;
+}
+
+void set_print(const struct set *set)
+{
+	const char *type;
+
+	type = set->flags & SET_F_MAP ? "map" : "set";
+	printf("\t%s %s {\n", type, set->handle.set);
+
+	printf("\t\ttype %s", set->keytype->name);
+	if (set->flags & SET_F_MAP)
+		printf(" => %s", set->datatype->name);
+	printf("\n");
+
+	if (set->flags & SET_F_ANONYMOUS)
+		printf("\t\tanonymous\n");
+	if (set->flags & SET_F_CONSTANT)
+		printf("\t\tconstant\n");
+	if (set->flags & SET_F_INTERVAL)
+		printf("\t\tinterval\n");
+
+	if (set->init != NULL && set->init->size > 0) {
+		printf("\t\telements = ");
+		expr_print(set->init);
+		printf("\n");
+	}
+	printf("\t}\n");
+}
+
 struct rule *rule_alloc(const struct location *loc, const struct handle *h)
 {
 	struct rule *rule;
@@ -168,6 +239,7 @@ struct table *table_alloc(void)
 
 	table = xzalloc(sizeof(*table));
 	init_list_head(&table->chains);
+	init_list_head(&table->sets);
 	return table;
 }
 
@@ -204,9 +276,17 @@ struct table *table_lookup(const struct handle *h)
 static void table_print(const struct table *table)
 {
 	struct chain *chain;
+	struct set *set;
 	const char *delim = "";
 
 	printf("table %s {\n", table->handle.table);
+	list_for_each_entry(set, &table->sets, list) {
+		if (set->flags & SET_F_ANONYMOUS)
+			continue;
+		printf("%s", delim);
+		set_print(set);
+		delim = "\n";
+	}
 	list_for_each_entry(chain, &table->chains, list) {
 		printf("%s", delim);
 		chain_print(chain);
@@ -233,6 +313,12 @@ void cmd_free(struct cmd *cmd)
 	handle_free(&cmd->handle);
 	if (cmd->data != NULL) {
 		switch (cmd->obj) {
+		case CMD_OBJ_SETELEM:
+			expr_free(cmd->expr);
+			break;
+		case CMD_OBJ_SET:
+			set_free(cmd->set);
+			break;
 		case CMD_OBJ_RULE:
 			rule_free(cmd->rule);
 			break;
@@ -267,14 +353,42 @@ static int do_add_chain(struct netlink_ctx *ctx, const struct handle *h,
 	return 0;
 }
 
+static int do_add_setelems(struct netlink_ctx *ctx, const struct handle *h,
+			   const struct expr *expr)
+{
+	if (netlink_add_setelems(ctx, h, expr) < 0)
+		return -1;
+	return 0;
+}
+
+static int do_add_set(struct netlink_ctx *ctx, const struct handle *h,
+		      struct set *set)
+{
+	if (netlink_add_set(ctx, h, set) < 0)
+		return -1;
+	if (set->init != NULL) {
+		if (set->flags & SET_F_INTERVAL)
+			set_to_intervals(set->init);
+		if (do_add_setelems(ctx, &set->handle, set->init) < 0)
+			return -1;
+	}
+	return 0;
+}
+
 static int do_add_table(struct netlink_ctx *ctx, const struct handle *h,
 			struct table *table)
 {
 	struct chain *chain;
+	struct set *set;
 
 	if (netlink_add_table(ctx, h, table) < 0)
 		return -1;
 	if (table != NULL) {
+		list_for_each_entry(set, &table->sets, list) {
+			handle_merge(&set->handle, &table->handle);
+			if (do_add_set(ctx, &set->handle, set) < 0)
+				return -1;
+		}
 		list_for_each_entry(chain, &table->chains, list) {
 			if (do_add_chain(ctx, &chain->handle, chain) < 0)
 				return -1;
@@ -292,6 +406,10 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd)
 		return do_add_chain(ctx, &cmd->handle, cmd->chain);
 	case CMD_OBJ_RULE:
 		return netlink_add_rule(ctx, &cmd->handle, cmd->rule);
+	case CMD_OBJ_SET:
+		return do_add_set(ctx, &cmd->handle, cmd->set);
+	case CMD_OBJ_SETELEM:
+		return do_add_setelems(ctx, &cmd->handle, cmd->expr);
 	default:
 		BUG();
 	}
@@ -307,39 +425,76 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
 		return netlink_delete_chain(ctx, &cmd->handle);
 	case CMD_OBJ_RULE:
 		return netlink_delete_rule(ctx, &cmd->handle);
+	case CMD_OBJ_SET:
+		return netlink_delete_set(ctx, &cmd->handle);
+	case CMD_OBJ_SETELEM:
+		return netlink_delete_setelems(ctx, &cmd->handle, cmd->expr);
 	default:
 		BUG();
 	}
 }
 
+static int do_list_sets(struct netlink_ctx *ctx, struct table *table)
+{
+	struct set *set, *nset;
+
+	if (netlink_list_sets(ctx, &table->handle) < 0)
+		return -1;
+
+	list_for_each_entry_safe(set, nset, &ctx->list, list) {
+		if (set->flags & SET_F_ANONYMOUS &&
+		    netlink_get_setelems(ctx, &set->handle, set) < 0)
+			return -1;
+		list_move_tail(&set->list, &table->sets);
+	}
+	return 0;
+}
+
 static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 {
 	struct table *table;
 	struct chain *chain;
-	struct rule *rule, *next;
+	struct rule *rule, *nrule;
+	struct set *set, *nset;
+
+	table = table_alloc();
+	handle_merge(&table->handle, &cmd->handle);
+	table_add_hash(table);
 
 	switch (cmd->obj) {
 	case CMD_OBJ_TABLE:
+		if (do_list_sets(ctx, table) < 0)
+			return -1;
 		if (netlink_list_table(ctx, &cmd->handle) < 0)
 			return -1;
 		break;
 	case CMD_OBJ_CHAIN:
+		if (do_list_sets(ctx, table) < 0)
+			return -1;
 		if (netlink_list_chain(ctx, &cmd->handle) < 0)
 			return -1;
 		break;
+	case CMD_OBJ_SETS:
+		if (netlink_list_sets(ctx, &cmd->handle) < 0)
+			return -1;
+		list_for_each_entry_safe(set, nset, &ctx->list, list)
+			list_move_tail(&set->list, &table->sets);
+		break;
+	case CMD_OBJ_SET:
+		if (netlink_get_set(ctx, &cmd->handle) < 0)
+			return -1;
+		list_for_each_entry(set, &ctx->list, list) {
+			if (netlink_get_setelems(ctx, &cmd->handle, set) < 0)
+				return -1;
+			set_print(set);
+		}
+		return 0;
 	default:
 		BUG();
 	}
 
-	table = NULL;
-	list_for_each_entry_safe(rule, next, &ctx->list, list) {
+	list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
 		table = table_lookup(&rule->handle);
-		if (table == NULL) {
-			table = table_alloc();
-			handle_merge(&table->handle, &rule->handle);
-			table_add_hash(table);
-		}
-
 		chain = chain_lookup(table, &rule->handle);
 		if (chain == NULL) {
 			chain = chain_alloc(rule->handle.chain);
@@ -349,10 +504,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
 		list_move_tail(&rule->list, &chain->rules);
 	}
 
-	if (table != NULL)
-		table_print(table);
-	else
-		printf("table %s does not exist\n", cmd->handle.table);
+	table_print(table);
 	return 0;
 }
 
diff --git a/src/scanner.l b/src/scanner.l
index f8d018b..de2373d 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -210,9 +210,7 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "$"			{ return '$'; }
 "="			{ return '='; }
 "=>"			{ return ARROW; }
-"map"			{ return MAP; }
 "vmap"			{ return VMAP; }
-"set"			{ return SET; }
 
 "NF_INET_PRE_ROUTING"	{ yylval->val = NF_INET_PRE_ROUTING;	return HOOKNUM; }
 "NF_INET_LOCAL_IN"	{ yylval->val = NF_INET_LOCAL_IN;	return HOOKNUM; }
@@ -229,6 +227,10 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "table"			{ return TABLE; }
 "chain"			{ return CHAIN; }
 "rule"			{ return RULE; }
+"sets"			{ return SETS; }
+"set"			{ return SET; }
+"element"		{ return ELEMENT; }
+"map"			{ return MAP; }
 "handle"		{ return HANDLE; }
 
 "accept"		{ return ACCEPT; }



More information about the netfilter-cvslog mailing list