[nftables libnl] libnl: nf_tables: add attribute tracking and test functions for expressions

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


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

    libnl: nf_tables: add attribute tracking and test functions for expressions
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>
       via  2d9fbc9decdd1f6222d31392d9cd71fbc91eaa4f (commit)
      from  0917b23733f46c65703545f82cc633836436f13b (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 2d9fbc9decdd1f6222d31392d9cd71fbc91eaa4f
Author: Patrick McHardy <kaber at trash.net>
Date:   Wed Apr 8 06:19:10 2009 +0200

    libnl: nf_tables: add attribute tracking and test functions for expressions
    
    Signed-off-by: Patrick McHardy <kaber at trash.net>

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

 include/netlink/netfilter/nft-expr-modules.h |    2 +
 include/netlink/netfilter/nft_expr.h         |   58 ++++++++++++++-
 lib/netfilter/nft_bitwise_expr.c             |  108 ++++++++++++++++++++------
 lib/netfilter/nft_byteorder_expr.c           |   99 ++++++++++++++++++------
 lib/netfilter/nft_cmp_expr.c                 |   78 ++++++++++++++-----
 lib/netfilter/nft_counter_expr.c             |   55 +++++++++++--
 lib/netfilter/nft_ct_expr.c                  |   58 +++++++++++---
 lib/netfilter/nft_expr_obj.c                 |   13 +++-
 lib/netfilter/nft_exthdr_expr.c              |   85 ++++++++++++++++-----
 lib/netfilter/nft_immediate_expr.c           |   63 ++++++++++++---
 lib/netfilter/nft_limit_expr.c               |   67 +++++++++++------
 lib/netfilter/nft_log_expr.c                 |  104 +++++++++++++++++--------
 lib/netfilter/nft_lookup_expr.c              |   65 ++++++++++++----
 lib/netfilter/nft_meta_expr.c                |   57 +++++++++++---
 lib/netfilter/nft_nat_expr.c                 |  108 ++++++++++++++++++--------
 lib/netfilter/nft_payload_expr.c             |   86 ++++++++++++++++-----
 16 files changed, 848 insertions(+), 258 deletions(-)
Signed-off-by: Patrick McHardy <kaber at trash.net>

diff --git a/include/netlink/netfilter/nft-expr-modules.h b/include/netlink/netfilter/nft-expr-modules.h
index 2e83073..6f91596 100644
--- a/include/netlink/netfilter/nft-expr-modules.h
+++ b/include/netlink/netfilter/nft-expr-modules.h
@@ -59,6 +59,8 @@ struct nft_expr_ops
 	 */
 	int  (*eo_clone)(struct nfnl_nft_expr *, struct nfnl_nft_expr *);
 
+	char *(*eo_attrs2str)(int, char *, size_t);
+
 	/**
 	 * INTERNAL (Do not use)
 	 */
diff --git a/include/netlink/netfilter/nft_expr.h b/include/netlink/netfilter/nft_expr.h
index b7895f0..0e98e4b 100644
--- a/include/netlink/netfilter/nft_expr.h
+++ b/include/netlink/netfilter/nft_expr.h
@@ -31,76 +31,100 @@ extern int			nfnl_nft_expr_build_message(struct nl_msg *,
 extern int			nfnl_nft_immediate_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_immediate_set_dreg(struct nfnl_nft_expr *,
 							    enum nft_registers);
+extern int			nfnl_nft_immediate_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_immediate_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_immediate_set_data(struct nfnl_nft_expr *,
 							    struct nfnl_nft_data *);
+extern int			nfnl_nft_immediate_test_data(const struct nfnl_nft_expr *);
 extern struct nfnl_nft_data *	nfnl_nft_immediate_get_data(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_bitwise_init(struct nfnl_nft_expr *expr);
 extern void			nfnl_nft_bitwise_set_sreg(struct nfnl_nft_expr *,
 							  enum nft_registers);
+extern int			nfnl_nft_bitwise_test_sreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_bitwise_get_sreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_bitwise_set_dreg(struct nfnl_nft_expr *,
 							  enum nft_registers);
+extern int			nfnl_nft_bitwise_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_bitwise_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_bitwise_set_len(struct nfnl_nft_expr *,
 							 unsigned int);
+extern int			nfnl_nft_bitwise_test_len(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_bitwise_get_len(const struct nfnl_nft_expr *);
-
 extern void			nfnl_nft_bitwise_set_mask(struct nfnl_nft_expr *,
 							  struct nfnl_nft_data *);
+extern int			nfnl_nft_bitwise_test_mask(const struct nfnl_nft_expr *);
 extern struct nfnl_nft_data *	nfnl_nft_bitwise_get_mask(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_bitwise_set_xor(struct nfnl_nft_expr *,
 							 struct nfnl_nft_data *);
+extern int			nfnl_nft_bitwise_test_xor(const struct nfnl_nft_expr *);
 extern struct nfnl_nft_data *	nfnl_nft_bitwise_get_xor(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_byteorder_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_byteorder_set_sreg(struct nfnl_nft_expr *,
 							    enum nft_registers);
+extern int			nfnl_nft_byteorder_test_sreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_byteorder_get_sreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_byteorder_set_dreg(struct nfnl_nft_expr *,
 							    enum nft_registers);
+extern int			nfnl_nft_byteorder_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_byteorder_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_byteorder_set_op(struct nfnl_nft_expr *,
 							  enum nft_byteorder_ops);
+extern int			nfnl_nft_byteorder_test_op(const struct nfnl_nft_expr *);
 extern enum nft_byteorder_ops	nfnl_nft_byteorder_get_op(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_byteorder_set_len(struct nfnl_nft_expr *,
 							   unsigned int);
+extern int			nfnl_nft_byteorder_test_len(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_byteoder_get_len(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_byteorder_set_size(struct nfnl_nft_expr *,
 							    unsigned int);
+extern int			nfnl_nft_byteorder_test_size(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_byteorder_get_size(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_cmp_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_cmp_set_sreg(struct nfnl_nft_expr *,
 						      enum nft_registers);
+extern int			nfnl_nft_cmp_test_sreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_cmp_get_sreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_cmp_set_op(struct nfnl_nft_expr *,
 						    enum nft_cmp_ops);
+extern int			nfnl_nft_cmp_test_op(const struct nfnl_nft_expr *);
 extern enum nft_cmp_ops		nfnl_nft_cmp_get_op(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_cmp_set_data(struct nfnl_nft_expr *,
 						      struct nfnl_nft_data *);
+extern int			nfnl_nft_cmp_test_data(const struct nfnl_nft_expr *);
 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_lookup_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_lookup_set_set(struct nfnl_nft_expr *,
 							const char *);
+extern int			nfnl_nft_lookup_test_set(const struct nfnl_nft_expr *);
 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 int			nfnl_nft_lookup_test_sreg(const struct nfnl_nft_expr *);
 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 int			nfnl_nft_lookup_test_dreg(const struct nfnl_nft_expr *);
 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 *,
 						       enum nft_registers);
+extern int			nfnl_nft_meta_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_meta_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_meta_set_key(struct nfnl_nft_expr *,
 						      enum nft_meta_keys);
+extern int			nfnl_nft_meta_test_key(const struct nfnl_nft_expr *);
 extern enum nft_meta_keys	nfnl_nft_meta_get_key(const struct nfnl_nft_expr *);
 extern char *			nfnl_nft_meta_key2str(enum nft_meta_keys,
 						      char *, size_t);
@@ -110,9 +134,11 @@ extern enum nft_meta_keys	nfnl_nft_meta_str2key(const char *);
 extern int			nfnl_nft_ct_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_ct_set_dreg(struct nfnl_nft_expr *,
 						     enum nft_registers);
+extern int			nfnl_nft_ct_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_ct_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_ct_set_key(struct nfnl_nft_expr *,
 						    enum nft_ct_keys);
+extern int			nfnl_nft_ct_test_key(const struct nfnl_nft_expr *);
 extern enum nft_ct_keys		nfnl_nft_ct_get_key(const struct nfnl_nft_expr *);
 extern char *			nfnl_nft_ct_key2str(enum nft_ct_keys, char *,
 						    size_t);
@@ -122,72 +148,102 @@ extern enum nft_ct_keys		nfnl_nft_ct_str2key(const char *);
 extern int			nfnl_nft_payload_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_payload_set_dreg(struct nfnl_nft_expr *,
 							  enum nft_registers);
+extern int			nfnl_nft_payload_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_payload_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_payload_set_base(struct nfnl_nft_expr *,
 							  enum nft_payload_bases);
+extern int			nfnl_nft_payload_test_base(const struct nfnl_nft_expr *);
 extern enum nft_payload_bases	nfnl_nft_payload_get_base(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_payload_set_offset(struct nfnl_nft_expr *,
 							    unsigned int);
+extern int			nfnl_nft_payload_test_offset(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_payload_get_offset(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_payload_set_len(struct nfnl_nft_expr *,
 							 unsigned int);
+extern int			nfnl_nft_payload_test_len(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_payload_get_len(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_exthdr_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_exthdr_set_dreg(struct nfnl_nft_expr *,
 							 enum nft_registers);
+extern int			nfnl_nft_exthdr_test_dreg(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_exthdr_get_dreg(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_exthdr_set_type(struct nfnl_nft_expr *,
 							 uint8_t type);
+extern int			nfnl_nft_exthdr_test_type(const struct nfnl_nft_expr *);
 extern uint8_t			nfnl_nft_exthdr_get_type(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_exthdr_set_offset(struct nfnl_nft_expr *,
 							   unsigned int);
+extern int			nfnl_nft_exthdr_test_offset(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_exthdr_get_offset(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_exthdr_set_len(struct nfnl_nft_expr *,
 							unsigned int);
+extern int			nfnl_nft_exthdr_test_len(const struct nfnl_nft_expr *);
 extern unsigned int		nfnl_nft_exthdr_get_len(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_counter_init(struct nfnl_nft_expr *);
+extern void			nfnl_nft_counter_set_packets(struct nfnl_nft_expr *,
+							     uint64_t);
+extern int			nfnl_nft_counter_test_packets(const struct nfnl_nft_expr *);
 extern uint64_t			nfnl_nft_counter_get_packets(const struct nfnl_nft_expr *);
+extern void			nfnl_nft_counter_set_bytes(struct nfnl_nft_expr *,
+							   uint64_t);
+extern int			nfnl_nft_counter_test_bytes(const struct nfnl_nft_expr *);
 extern uint64_t			nfnl_nft_counter_get_bytes(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_log_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_log_set_prefix(struct nfnl_nft_expr *,
 							const char *);
+extern int			nfnl_nft_log_test_prefix(const struct nfnl_nft_expr *);
 extern const char *		nfnl_nft_log_get_prefix(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_log_set_group(struct nfnl_nft_expr *,
 						       uint32_t);
+extern int			nfnl_nft_log_test_group(const struct nfnl_nft_expr *);
 extern uint32_t			nfnl_nft_log_get_group(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_log_set_snaplen(struct nfnl_nft_expr *,
 							 uint32_t);
+extern int			nfnl_nft_log_test_snaplen(const struct nfnl_nft_expr *);
 extern uint32_t			nfnl_nft_log_get_snaplen(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_log_set_qthreshold(struct nfnl_nft_expr *,
 							    uint32_t);
+extern int			nfnl_nft_log_test_qthreshold(const struct nfnl_nft_expr *);
 extern uint32_t			nfnl_nft_log_get_qthreshold(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_limit_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_limit_set_rate(struct nfnl_nft_expr *,
 							uint64_t);
+extern int			nfnl_nft_limit_test_rate(const struct nfnl_nft_expr *);
 extern uint64_t			nfnl_nft_limit_get_rate(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_limit_set_depth(struct nfnl_nft_expr *,
 							 uint64_t);
+extern int			nfnl_nft_limit_test_depth(const struct nfnl_nft_expr *);
 extern uint64_t			nfnl_nft_limit_get_depth(const struct nfnl_nft_expr *);
 
+
 extern int			nfnl_nft_nat_init(struct nfnl_nft_expr *);
 extern void			nfnl_nft_nat_set_type(struct nfnl_nft_expr *,
 						      enum nft_nat_types);
+extern int			nfnl_nft_nat_test_type(const struct nfnl_nft_expr *);
 extern enum nft_nat_types	nfnl_nft_nat_get_type(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_nat_set_sreg_addr_min(struct nfnl_nft_expr *,
 							       enum nft_registers);
+extern int			nfnl_nft_nat_test_sreg_addr_min(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_nat_get_sreg_addr_min(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_nat_set_sreg_addr_max(struct nfnl_nft_expr *,
 							       enum nft_registers);
+extern int			nfnl_nft_nat_test_sreg_addr_max(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_nat_get_sreg_addr_max(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_nat_set_sreg_proto_min(struct nfnl_nft_expr *,
 							        enum nft_registers);
+extern int			nfnl_nft_nat_test_sreg_proto_min(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_nat_get_sreg_proto_min(const struct nfnl_nft_expr *);
 extern void			nfnl_nft_nat_set_sreg_proto_max(struct nfnl_nft_expr *,
 							        enum nft_registers);
+extern int			nfnl_nft_nat_test_sreg_proto_max(const struct nfnl_nft_expr *);
 extern enum nft_registers	nfnl_nft_nat_get_sreg_proto_max(const struct nfnl_nft_expr *);
 
 #ifdef __cplusplus
diff --git a/lib/netfilter/nft_bitwise_expr.c b/lib/netfilter/nft_bitwise_expr.c
index 1fdebaa..ca9d27f 100644
--- a/lib/netfilter/nft_bitwise_expr.c
+++ b/lib/netfilter/nft_bitwise_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -22,6 +22,14 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define BITWISE_ATTR_SREG	(1UL << 0)
+#define BITWISE_ATTR_DREG	(1UL << 1)
+#define BITWISE_ATTR_LEN	(1UL << 2)
+#define BITWISE_ATTR_MASK	(1UL << 3)
+#define BITWISE_ATTR_XOR	(1UL << 4)
+/** @endcond */
+
 struct nft_bitwise_expr {
 	enum nft_registers	sreg;
 	enum nft_registers	dreg;
@@ -72,25 +80,28 @@ static void nft_bitwise_free_data(struct nfnl_nft_expr *expr)
 static int nft_bitwise_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 {
 	struct nft_bitwise_expr *bitwise;
+	struct nfnl_nft_data *data;
 
 	bitwise = nft_bitwise_expr_alloc(expr);
 	if (bitwise == NULL)
 		return -ENOMEM;
 
 	if (tb[NFTA_BITWISE_SREG])
-		bitwise->sreg = ntohl(nla_get_u32(tb[NFTA_BITWISE_SREG]));
+		nfnl_nft_bitwise_set_sreg(expr, ntohl(nla_get_u32(tb[NFTA_BITWISE_SREG])));
 	if (tb[NFTA_BITWISE_DREG])
-		bitwise->dreg = ntohl(nla_get_u32(tb[NFTA_BITWISE_DREG]));
+		nfnl_nft_bitwise_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_BITWISE_DREG])));
 	if (tb[NFTA_BITWISE_LEN])
-		bitwise->len = ntohl(nla_get_u32(tb[NFTA_BITWISE_LEN]));
+		nfnl_nft_bitwise_set_len(expr, ntohl(nla_get_u32(tb[NFTA_BITWISE_LEN])));
 
 	if (tb[NFTA_BITWISE_MASK]) {
-		if (nfnl_nft_data_parse(tb[NFTA_BITWISE_MASK], &bitwise->mask) < 0)
+		if (nfnl_nft_data_parse(tb[NFTA_BITWISE_MASK], &data) < 0)
 			goto err1;
+		nfnl_nft_bitwise_set_mask(expr, data);
 	}
 	if (tb[NFTA_BITWISE_XOR]) {
-		if (nfnl_nft_data_parse(tb[NFTA_BITWISE_XOR], &bitwise->xor) < 0)
+		if (nfnl_nft_data_parse(tb[NFTA_BITWISE_XOR], &data) < 0)
 			goto err2;
+		nfnl_nft_bitwise_set_xor(expr, data);
 	}
 	return 0;
 
@@ -104,20 +115,22 @@ static int nft_bitwise_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_bitwise_expr *bitwise = nft_bitwise(expr);
 
-	if (nla_put_u32(msg, NFTA_BITWISE_SREG, htonl(bitwise->sreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BITWISE_DREG, htonl(bitwise->dreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BITWISE_LEN, htonl(bitwise->len)) < 0)
-		goto errout;
-
-	if (nfnl_nft_data_put(msg, NFTA_BITWISE_MASK, bitwise->mask) < 0)
-		goto errout;
-	if (nfnl_nft_data_put(msg, NFTA_BITWISE_XOR, bitwise->xor) < 0)
-		goto errout;
+	if (nfnl_nft_bitwise_test_sreg(expr))
+		NLA_PUT_U32(msg, NFTA_BITWISE_SREG, htonl(bitwise->sreg));
+	if (nfnl_nft_bitwise_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_BITWISE_DREG, htonl(bitwise->dreg));
+	if (nfnl_nft_bitwise_test_len(expr))
+		NLA_PUT_U32(msg, NFTA_BITWISE_LEN, htonl(bitwise->len));
+
+	if (nfnl_nft_bitwise_test_mask(expr) &&
+	    nfnl_nft_data_put(msg, NFTA_BITWISE_MASK, bitwise->mask) < 0)
+		goto nla_put_failure;
+	if (nfnl_nft_bitwise_test_xor(expr) &&
+	    nfnl_nft_data_put(msg, NFTA_BITWISE_XOR, bitwise->xor) < 0)
+		goto nla_put_failure;
 	return 0;
 
-errout:
+nla_put_failure:
 	return -1;
 }
 
@@ -138,9 +151,29 @@ int nfnl_nft_bitwise_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_bitwise_attrs[] = {
+	__ADD(BITWISE_ATTR_SREG,		sreg)
+	__ADD(BITWISE_ATTR_DREG,		dreg)
+	__ADD(BITWISE_ATTR_LEN,			len)
+	__ADD(BITWISE_ATTR_MASK,		mask)
+	__ADD(BITWISE_ATTR_XOR,			xor)
+};
+
+static char *nft_bitwise_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_bitwise_attrs,
+			   ARRAY_SIZE(nft_bitwise_attrs));
+}
+
 void nfnl_nft_bitwise_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_bitwise(expr)->sreg = reg;
+	expr->ce_mask |= BITWISE_ATTR_SREG;
+}
+
+int nfnl_nft_bitwise_test_sreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BITWISE_ATTR_SREG);
 }
 
 enum nft_registers nfnl_nft_bitwise_get_sreg(const struct nfnl_nft_expr *expr)
@@ -151,6 +184,12 @@ enum nft_registers nfnl_nft_bitwise_get_sreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_bitwise_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_bitwise(expr)->dreg = reg;
+	expr->ce_mask |= BITWISE_ATTR_DREG;
+}
+
+int nfnl_nft_bitwise_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BITWISE_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_bitwise_get_dreg(const struct nfnl_nft_expr *expr)
@@ -161,6 +200,12 @@ enum nft_registers nfnl_nft_bitwise_get_dreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_bitwise_set_len(struct nfnl_nft_expr *expr, unsigned int len)
 {
 	nft_bitwise(expr)->len = len;
+	expr->ce_mask |= BITWISE_ATTR_LEN;
+}
+
+int nfnl_nft_bitwise_test_len(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BITWISE_ATTR_LEN);
 }
 
 unsigned int nfnl_nft_bitwise_get_len(const struct nfnl_nft_expr *expr)
@@ -171,6 +216,12 @@ unsigned int nfnl_nft_bitwise_get_len(const struct nfnl_nft_expr *expr)
 void nfnl_nft_bitwise_set_mask(struct nfnl_nft_expr *expr, struct nfnl_nft_data *data)
 {
 	nft_bitwise(expr)->mask = data;
+	expr->ce_mask |= BITWISE_ATTR_MASK;
+}
+
+int nfnl_nft_bitwise_test_mask(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BITWISE_ATTR_MASK);
 }
 
 struct nfnl_nft_data *nfnl_nft_bitwise_get_mask(const struct nfnl_nft_expr *expr)
@@ -181,6 +232,12 @@ struct nfnl_nft_data *nfnl_nft_bitwise_get_mask(const struct nfnl_nft_expr *expr
 void nfnl_nft_bitwise_set_xor(struct nfnl_nft_expr *expr, struct nfnl_nft_data *data)
 {
 	nft_bitwise(expr)->xor = data;
+	expr->ce_mask |= BITWISE_ATTR_XOR;
+}
+
+int nfnl_nft_bitwise_test_xor(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BITWISE_ATTR_XOR);
 }
 
 struct nfnl_nft_data *nfnl_nft_bitwise_get_xor(const struct nfnl_nft_expr *expr)
@@ -189,14 +246,15 @@ struct nfnl_nft_data *nfnl_nft_bitwise_get_xor(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops bitwise_expr_ops = {
-	.eo_name		= "bitwise",
+	.eo_name			= "bitwise",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_bitwise_dump,
-	.eo_get_opts		= nft_bitwise_get_opts,
-	.eo_msg_parser		= nft_bitwise_msg_parser,
-	.eo_free_data		= nft_bitwise_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_bitwise_policy,
-	.eo_maxattr		= NFTA_BITWISE_MAX,
+	.eo_get_opts			= nft_bitwise_get_opts,
+	.eo_msg_parser			= nft_bitwise_msg_parser,
+	.eo_free_data			= nft_bitwise_free_data,
+	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_bitwise_attrs2str,
+	.eo_policy			= nft_bitwise_policy,
+	.eo_maxattr			= NFTA_BITWISE_MAX,
 };
 
 static void __init nft_bitwise_expr_init(void)
diff --git a/lib/netfilter/nft_byteorder_expr.c b/lib/netfilter/nft_byteorder_expr.c
index 04fc6be..8cf5972 100644
--- a/lib/netfilter/nft_byteorder_expr.c
+++ b/lib/netfilter/nft_byteorder_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -22,6 +22,14 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define BYTEORDER_ATTR_SREG	(1UL << 0)
+#define BYTEORDER_ATTR_DREG	(1UL << 1)
+#define BYTEORDER_ATTR_OP	(1UL << 2)
+#define BYTEORDER_ATTR_LEN	(1UL << 3)
+#define BYTEORDER_ATTR_SIZE	(1UL << 4)
+/** @endcond */
+
 struct nft_byteorder_expr {
 	enum nft_registers	sreg;
 	enum nft_registers	dreg;
@@ -70,15 +78,15 @@ static int nft_byteorder_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *t
 		return -ENOMEM;
 
 	if (tb[NFTA_BYTEORDER_SREG])
-		byteorder->sreg = ntohl(nla_get_u32(tb[NFTA_BYTEORDER_SREG]));
+		nfnl_nft_byteorder_set_sreg(expr, ntohl(nla_get_u32(tb[NFTA_BYTEORDER_SREG])));
 	if (tb[NFTA_BYTEORDER_DREG])
-		byteorder->dreg = ntohl(nla_get_u32(tb[NFTA_BYTEORDER_DREG]));
+		nfnl_nft_byteorder_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_BYTEORDER_DREG])));
 	if (tb[NFTA_BYTEORDER_LEN])
-		byteorder->len = ntohl(nla_get_u32(tb[NFTA_BYTEORDER_LEN]));
+		nfnl_nft_byteorder_set_len(expr, ntohl(nla_get_u32(tb[NFTA_BYTEORDER_LEN])));
 	if (tb[NFTA_BYTEORDER_OP])
-		byteorder->op = ntohl(nla_get_u32(tb[NFTA_BYTEORDER_OP]));
+		nfnl_nft_byteorder_set_op(expr, ntohl(nla_get_u32(tb[NFTA_BYTEORDER_OP])));
 	if (tb[NFTA_BYTEORDER_SIZE])
-		byteorder->size = ntohl(nla_get_u32(tb[NFTA_BYTEORDER_SIZE]));
+		nfnl_nft_byteorder_set_size(expr, ntohl(nla_get_u32(tb[NFTA_BYTEORDER_SIZE])));
 
 	return 0;
 }
@@ -87,19 +95,19 @@ static int nft_byteorder_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr
 {
 	struct nft_byteorder_expr *byteorder = nft_byteorder(expr);
 
-	if (nla_put_u32(msg, NFTA_BYTEORDER_SREG, htonl(byteorder->sreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BYTEORDER_DREG, htonl(byteorder->dreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BYTEORDER_LEN, htonl(byteorder->len) < 0))
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BYTEORDER_OP, htonl(byteorder->op)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_BYTEORDER_SIZE, htonl(byteorder->size)) < 0)
-		goto errout;
+	if (nfnl_nft_byteorder_test_sreg(expr))
+		NLA_PUT_U32(msg, NFTA_BYTEORDER_SREG, htonl(byteorder->sreg));
+	if (nfnl_nft_byteorder_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_BYTEORDER_DREG, htonl(byteorder->dreg));
+	if (nfnl_nft_byteorder_test_len(expr))
+		NLA_PUT_U32(msg, NFTA_BYTEORDER_LEN, htonl(byteorder->len));
+	if (nfnl_nft_byteorder_test_op(expr))
+		NLA_PUT_U32(msg, NFTA_BYTEORDER_OP, htonl(byteorder->op));
+	if (nfnl_nft_byteorder_test_size(expr))
+		NLA_PUT_U32(msg, NFTA_BYTEORDER_SIZE, htonl(byteorder->size));
 	return 0;
 
-errout:
+nla_put_failure:
 	return -1;
 }
 
@@ -123,9 +131,29 @@ int nfnl_nft_byteorder_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_byteorder_attrs[] = {
+	__ADD(BYTEORDER_ATTR_SREG,		sreg)
+	__ADD(BYTEORDER_ATTR_DREG,		dreg)
+	__ADD(BYTEORDER_ATTR_OP,		op)
+	__ADD(BYTEORDER_ATTR_LEN,		len)
+	__ADD(BYTEORDER_ATTR_SIZE,		size)
+};
+
+static char *nft_byteorder_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_byteorder_attrs,
+			   ARRAY_SIZE(nft_byteorder_attrs));
+}
+
 void nfnl_nft_byteorder_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_byteorder(expr)->sreg = reg;
+	expr->ce_mask |= BYTEORDER_ATTR_SREG;
+}
+
+int nfnl_nft_byteorder_test_sreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BYTEORDER_ATTR_SREG);
 }
 
 enum nft_registers nfnl_nft_byteorder_get_sreg(const struct nfnl_nft_expr *expr)
@@ -136,6 +164,12 @@ enum nft_registers nfnl_nft_byteorder_get_sreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_byteorder_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_byteorder(expr)->dreg = reg;
+	expr->ce_mask |= BYTEORDER_ATTR_DREG;
+}
+
+int nfnl_nft_byteorder_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BYTEORDER_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_byteorder_get_dreg(const struct nfnl_nft_expr *expr)
@@ -146,6 +180,12 @@ enum nft_registers nfnl_nft_byteorder_get_dreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_byteorder_set_op(struct nfnl_nft_expr *expr, enum nft_byteorder_ops op)
 {
 	nft_byteorder(expr)->op = op;
+	expr->ce_mask |= BYTEORDER_ATTR_OP;
+}
+
+int nfnl_nft_byteorder_test_op(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BYTEORDER_ATTR_OP);
 }
 
 enum nft_byteorder_ops nfnl_nft_byteorder_get_op(const struct nfnl_nft_expr *expr)
@@ -156,6 +196,12 @@ enum nft_byteorder_ops nfnl_nft_byteorder_get_op(const struct nfnl_nft_expr *exp
 void nfnl_nft_byteorder_set_len(struct nfnl_nft_expr *expr, unsigned int len)
 {
 	nft_byteorder(expr)->len = len;
+	expr->ce_mask |= BYTEORDER_ATTR_LEN;
+}
+
+int nfnl_nft_byteorder_test_len(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BYTEORDER_ATTR_LEN);
 }
 
 unsigned int nfnl_nft_byteoder_get_len(const struct nfnl_nft_expr *expr)
@@ -166,6 +212,12 @@ unsigned int nfnl_nft_byteoder_get_len(const struct nfnl_nft_expr *expr)
 void nfnl_nft_byteorder_set_size(struct nfnl_nft_expr *expr, unsigned int size)
 {
 	nft_byteorder(expr)->size = size;
+	expr->ce_mask |= BYTEORDER_ATTR_SIZE;
+}
+
+int nfnl_nft_byteorder_test_size(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & BYTEORDER_ATTR_SIZE);
 }
 
 unsigned int nfnl_nft_byteorder_get_size(const struct nfnl_nft_expr *expr)
@@ -174,13 +226,14 @@ unsigned int nfnl_nft_byteorder_get_size(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops byteorder_expr_ops = {
-	.eo_name		= "byteorder",
+	.eo_name			= "byteorder",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_byteorder_dump,
-	.eo_get_opts		= nft_byteorder_get_opts,
-	.eo_msg_parser		= nft_byteorder_msg_parser,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_byteorder_policy,
-	.eo_maxattr		= NFTA_BYTEORDER_MAX,
+	.eo_get_opts			= nft_byteorder_get_opts,
+	.eo_msg_parser			= nft_byteorder_msg_parser,
+	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_byteorder_attrs2str,
+	.eo_policy			= nft_byteorder_policy,
+	.eo_maxattr			= NFTA_BYTEORDER_MAX,
 };
 
 static void __init nft_byteorder_expr_init(void)
diff --git a/lib/netfilter/nft_cmp_expr.c b/lib/netfilter/nft_cmp_expr.c
index f3eaefe..3ed9296 100644
--- a/lib/netfilter/nft_cmp_expr.c
+++ b/lib/netfilter/nft_cmp_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -22,6 +22,12 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define CMP_ATTR_SREG		(1UL << 0)
+#define CMP_ATTR_OP		(1UL << 1)
+#define CMP_ATTR_DATA		(1UL << 2)
+/** @endcond */
+
 struct nft_cmp_expr {
 	struct nfnl_nft_data *	data;
 	enum nft_registers	sreg;
@@ -67,20 +73,21 @@ static void nft_cmp_free_data(struct nfnl_nft_expr *expr)
 static int nft_cmp_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 {
 	struct nft_cmp_expr *cmp;
+	struct nfnl_nft_data *data;
 
 	cmp = nft_cmp_expr_alloc(expr);
 	if (cmp == NULL)
 		return -ENOMEM;
 
 	if (tb[NFTA_CMP_SREG])
-		cmp->sreg = ntohl(nla_get_u32(tb[NFTA_CMP_SREG]));
+		nfnl_nft_cmp_set_sreg(expr, ntohl(nla_get_u32(tb[NFTA_CMP_SREG])));
 	if (tb[NFTA_CMP_OP])
-		cmp->op = ntohl(nla_get_u32(tb[NFTA_CMP_OP]));
+		nfnl_nft_cmp_set_op(expr, ntohl(nla_get_u32(tb[NFTA_CMP_OP])));
 	if (tb[NFTA_CMP_DATA]) {
-		if (nfnl_nft_data_parse(tb[NFTA_CMP_DATA], &cmp->data) < 0)
+		if (nfnl_nft_data_parse(tb[NFTA_CMP_DATA], &data) < 0)
 			goto errout;
+		nfnl_nft_cmp_set_data(expr, data);
 	}
-
 	return 0;
 
 errout:
@@ -91,16 +98,16 @@ static int nft_cmp_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_cmp_expr *cmp = nft_cmp(expr);
 
-	if (nla_put_u32(msg, NFTA_CMP_SREG, htonl(cmp->sreg)) < 0)
-		goto errout;
-	if (nla_put_u32(msg, NFTA_CMP_OP, htonl(cmp->op)) < 0)
-		goto errout;
-	if (nfnl_nft_data_put(msg, NFTA_CMP_DATA, cmp->data) < 0)
-		goto errout;
-
+	if (nfnl_nft_cmp_test_sreg(expr))
+		NLA_PUT_U32(msg, NFTA_CMP_SREG, htonl(cmp->sreg));
+	if (nfnl_nft_cmp_test_op(expr))
+		NLA_PUT_U32(msg, NFTA_CMP_OP, htonl(cmp->op));
+	if (nfnl_nft_cmp_test_data(expr) &&
+	    nfnl_nft_data_put(msg, NFTA_CMP_DATA, cmp->data) < 0)
+		goto nla_put_failure;
 	return 0;
 
-errout:
+nla_put_failure:
 	return -1;
 }
 
@@ -122,9 +129,27 @@ int nfnl_nft_cmp_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_cmp_attrs[] = {
+	__ADD(CMP_ATTR_SREG,			sreg)
+	__ADD(CMP_ATTR_OP,			op)
+	__ADD(CMP_ATTR_DATA,			data)
+};
+
+static char *nft_cmp_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_cmp_attrs,
+			   ARRAY_SIZE(nft_cmp_attrs));
+}
+
 void nfnl_nft_cmp_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_cmp(expr)->sreg = reg;
+	expr->ce_mask |= CMP_ATTR_SREG;
+}
+
+int nfnl_nft_cmp_test_sreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & CMP_ATTR_SREG);
 }
 
 enum nft_registers nfnl_nft_cmp_get_sreg(const struct nfnl_nft_expr *expr)
@@ -135,6 +160,12 @@ enum nft_registers nfnl_nft_cmp_get_sreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_cmp_set_op(struct nfnl_nft_expr *expr, enum nft_cmp_ops op)
 {
 	nft_cmp(expr)->op = op;
+	expr->ce_mask |= CMP_ATTR_OP;
+}
+
+int nfnl_nft_cmp_test_op(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & CMP_ATTR_OP);
 }
 
 enum nft_cmp_ops nfnl_nft_cmp_get_op(const struct nfnl_nft_expr *expr)
@@ -145,6 +176,12 @@ enum nft_cmp_ops nfnl_nft_cmp_get_op(const struct nfnl_nft_expr *expr)
 void nfnl_nft_cmp_set_data(struct nfnl_nft_expr *expr, struct nfnl_nft_data *data)
 {
 	nft_cmp(expr)->data = data;
+	expr->ce_mask |= CMP_ATTR_DATA;
+}
+
+int nfnl_nft_cmp_test_data(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & CMP_ATTR_DATA);
 }
 
 struct nfnl_nft_data *nfnl_nft_cmp_get_data(const struct nfnl_nft_expr *expr)
@@ -172,14 +209,15 @@ enum nft_cmp_ops nfnl_nft_cmp_str2op(const char *name)
 }
 
 static struct nft_expr_ops cmp_expr_ops = {
-	.eo_name		= "cmp",
+	.eo_name			= "cmp",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_cmp_dump,
-	.eo_get_opts		= nft_cmp_get_opts,
-	.eo_msg_parser		= nft_cmp_msg_parser,
-	.eo_free_data		= nft_cmp_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_cmp_policy,
-	.eo_maxattr		= NFTA_CMP_MAX,
+	.eo_get_opts			= nft_cmp_get_opts,
+	.eo_msg_parser			= nft_cmp_msg_parser,
+	.eo_free_data			= nft_cmp_free_data,
+	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_cmp_attrs2str,
+	.eo_policy			= nft_cmp_policy,
+	.eo_maxattr			= NFTA_CMP_MAX,
 };
 
 static void __init nft_cmp_expr_init(void)
diff --git a/lib/netfilter/nft_counter_expr.c b/lib/netfilter/nft_counter_expr.c
index c05865d..806341b 100644
--- a/lib/netfilter/nft_counter_expr.c
+++ b/lib/netfilter/nft_counter_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,11 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define COUNTER_ATTR_BYTES	(1UL << 0)
+#define COUNTER_ATTR_PACKETS	(1UL << 1)
+/** @endcond */
+
 #if __BYTE_ORDER == __BIG_ENDIAN
 static uint64_t ntohll(uint64_t x)
 {
@@ -74,11 +79,9 @@ static int nft_counter_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[
 		return -ENOMEM;
 
 	if (tb[NFTA_COUNTER_BYTES])
-		counter->bytes = ntohll(nla_get_u64(tb[NFTA_COUNTER_BYTES]));
-
+		nfnl_nft_counter_set_bytes(expr, ntohll(nla_get_u64(tb[NFTA_COUNTER_BYTES])));
 	if (tb[NFTA_COUNTER_PACKETS])
-		counter->packets = ntohll(nla_get_u64(tb[NFTA_COUNTER_PACKETS]));
-
+		nfnl_nft_counter_set_packets(expr, ntohll(nla_get_u64(tb[NFTA_COUNTER_PACKETS])));
 	return 0;
 }
 
@@ -101,22 +104,56 @@ int nfnl_nft_counter_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_counter_attrs[] = {
+	__ADD(COUNTER_ATTR_BYTES,		bytes)
+	__ADD(COUNTER_ATTR_PACKETS,		packets)
+};
+
+static char *nft_counter_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_counter_attrs,
+			   ARRAY_SIZE(nft_counter_attrs));
+}
+
+void nfnl_nft_counter_set_packets(struct nfnl_nft_expr *expr, uint64_t packets)
+{
+	nft_counter(expr)->packets = packets;
+	expr->ce_mask |= COUNTER_ATTR_PACKETS;
+}
+
+int nfnl_nft_counter_test_packets(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & COUNTER_ATTR_PACKETS);
+}
+
 uint64_t nfnl_nft_counter_get_packets(const struct nfnl_nft_expr *expr)
 {
 	return nft_counter(expr)->packets;
 }
 
+void nfnl_nft_counter_set_bytes(struct nfnl_nft_expr *expr, uint64_t bytes)
+{
+	nft_counter(expr)->bytes = bytes;
+	expr->ce_mask |= COUNTER_ATTR_BYTES;
+}
+
+int nfnl_nft_counter_test_bytes(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & COUNTER_ATTR_BYTES);
+}
+
 uint64_t nfnl_nft_counter_get_bytes(const struct nfnl_nft_expr *expr)
 {
 	return nft_counter(expr)->bytes;
 }
 
 static struct nft_expr_ops counter_expr_ops = {
-	.eo_name		= "counter",
+	.eo_name			= "counter",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_counter_dump,
-	.eo_msg_parser		= nft_counter_msg_parser,
-	.eo_policy		= nft_counter_policy,
-	.eo_maxattr		= NFTA_COUNTER_MAX,
+	.eo_msg_parser			= nft_counter_msg_parser,
+	.eo_attrs2str			= nft_counter_attrs2str,
+	.eo_policy			= nft_counter_policy,
+	.eo_maxattr			= NFTA_COUNTER_MAX,
 };
 
 static void __init nft_counter_expr_init(void)
diff --git a/lib/netfilter/nft_ct_expr.c b/lib/netfilter/nft_ct_expr.c
index fa05076..8a11a4c 100644
--- a/lib/netfilter/nft_ct_expr.c
+++ b/lib/netfilter/nft_ct_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,12 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define CT_ATTR_DREG		(1UL << 0)
+#define CT_ATTR_KEY		(1UL << 1)
+#define CT_ATTR_DIRECTION	(1UL << 2)
+/** @endcond */
+
 struct nft_ct_expr {
 	enum nft_ct_keys	key;
 	enum nft_registers	dreg;
@@ -63,9 +69,9 @@ static int nft_ct_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 		return -ENOMEM;
 
 	if (tb[NFTA_CT_DREG])
-		ct->dreg = ntohl(nla_get_u32(tb[NFTA_CT_DREG]));
+		nfnl_nft_ct_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_CT_DREG])));
 	if (tb[NFTA_CT_KEY])
-		ct->key = ntohl(nla_get_u32(tb[NFTA_CT_KEY]));
+		nfnl_nft_ct_set_key(expr, ntohl(nla_get_u32(tb[NFTA_CT_KEY])));
 
 	return 0;
 }
@@ -74,11 +80,14 @@ static int nft_ct_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_ct_expr *ct = nft_ct(expr);
 
-	if (nla_put_u32(msg, NFTA_CT_DREG, htonl(ct->dreg)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_CT_KEY, htonl(ct->key)) < 0)
-		return -1;
+	if (nfnl_nft_ct_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_CT_DREG, htonl(ct->dreg));
+	if (nfnl_nft_ct_test_key(expr))
+		NLA_PUT_U32(msg, NFTA_CT_KEY, htonl(ct->key));
 	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static struct trans_tbl ct_keys[] = {
@@ -124,9 +133,27 @@ int nfnl_nft_ct_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_ct_attrs[] = {
+	__ADD(CT_ATTR_DREG,			dreg)
+	__ADD(CT_ATTR_KEY,			key)
+	__ADD(CT_ATTR_DIRECTION,		direction)
+};
+
+static char *nft_ct_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_ct_attrs,
+			   ARRAY_SIZE(nft_ct_attrs));
+}
+
 void nfnl_nft_ct_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_ct(expr)->dreg = reg;
+	expr->ce_mask |= CT_ATTR_DREG;
+}
+
+int nfnl_nft_ct_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & CT_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_ct_get_dreg(const struct nfnl_nft_expr *expr)
@@ -137,6 +164,12 @@ enum nft_registers nfnl_nft_ct_get_dreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_ct_set_key(struct nfnl_nft_expr *expr, enum nft_ct_keys key)
 {
 	nft_ct(expr)->key = key;
+	expr->ce_mask |= CT_ATTR_KEY;
+}
+
+int nfnl_nft_ct_test_key(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & CT_ATTR_KEY);
 }
 
 enum nft_ct_keys nfnl_nft_ct_get_key(const struct nfnl_nft_expr *expr)
@@ -145,12 +178,13 @@ enum nft_ct_keys nfnl_nft_ct_get_key(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops ct_expr_ops = {
-	.eo_name		= "ct",
+	.eo_name			= "ct",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_ct_dump,
-	.eo_get_opts		= nft_ct_get_opts,
-	.eo_msg_parser		= nft_ct_msg_parser,
-	.eo_policy		= nft_ct_policy,
-	.eo_maxattr		= NFTA_SET_MAX,
+	.eo_get_opts			= nft_ct_get_opts,
+	.eo_msg_parser			= nft_ct_msg_parser,
+	.eo_attrs2str			= nft_ct_attrs2str,
+	.eo_policy			= nft_ct_policy,
+	.eo_maxattr			= NFTA_SET_MAX,
 };
 
 static void __init nft_ct_expr_init(void)
diff --git a/lib/netfilter/nft_expr_obj.c b/lib/netfilter/nft_expr_obj.c
index 40e2634..b85a6c7 100644
--- a/lib/netfilter/nft_expr_obj.c
+++ b/lib/netfilter/nft_expr_obj.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -52,6 +52,16 @@ static void nft_expr_dump(struct nl_object *obj, struct nl_dump_params *p)
 	}
 }
 
+static char *nft_expr_attrs2str(int attrs, char *buf, size_t len)
+{
+	struct nl_object *obj = NULL; // FIXME
+	struct nfnl_nft_expr *expr = nl_object_priv(obj);
+
+	if (expr->expr_ops)
+		expr->expr_ops->eo_attrs2str(attrs, buf, len);
+	return NULL;
+}
+
 static int nft_expr_compare(struct nl_object *_a, struct nl_object *_b,
 			     uint32_t attrs, int flags)
 {
@@ -102,6 +112,7 @@ struct nl_object_ops nft_expr_obj_ops = {
 		[NL_DUMP_DETAILS]	= nft_expr_dump,
 		[NL_DUMP_STATS]		= nft_expr_dump,
 	},
+	.oo_attrs2str		= nft_expr_attrs2str,
 	.oo_compare		= nft_expr_compare,
 };
 
diff --git a/lib/netfilter/nft_exthdr_expr.c b/lib/netfilter/nft_exthdr_expr.c
index bb38434..052b691 100644
--- a/lib/netfilter/nft_exthdr_expr.c
+++ b/lib/netfilter/nft_exthdr_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,13 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define EXTHDR_ATTR_DREG	(1UL << 0)
+#define EXTHDR_ATTR_TYPE	(1UL << 1)
+#define EXTHDR_ATTR_OFFSET	(1UL << 2)
+#define EXTHDR_ATTR_LEN		(1UL << 3)
+/** @endcond */
+
 struct nft_exthdr_expr {
 	enum nft_registers	dreg;
 	uint8_t			type;
@@ -66,13 +73,13 @@ static int nft_exthdr_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[]
 		return -ENOMEM;
 
 	if (tb[NFTA_EXTHDR_DREG])
-		exthdr->dreg = ntohl(nla_get_u32(tb[NFTA_EXTHDR_DREG]));
+		nfnl_nft_exthdr_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_EXTHDR_DREG])));
 	if (tb[NFTA_EXTHDR_TYPE])
-		exthdr->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
+		nfnl_nft_exthdr_set_type(expr, nla_get_u8(tb[NFTA_EXTHDR_TYPE]));
 	if (tb[NFTA_EXTHDR_OFFSET])
-		exthdr->offset = ntohl(nla_get_u32(tb[NFTA_EXTHDR_OFFSET]));
+		nfnl_nft_exthdr_set_offset(expr, ntohl(nla_get_u32(tb[NFTA_EXTHDR_OFFSET])));
 	if (tb[NFTA_EXTHDR_LEN])
-		exthdr->len = ntohl(nla_get_u32(tb[NFTA_EXTHDR_LEN]));
+		nfnl_nft_exthdr_set_len(expr, ntohl(nla_get_u32(tb[NFTA_EXTHDR_LEN])));
 
 	return 0;
 }
@@ -81,16 +88,18 @@ static int nft_exthdr_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_exthdr_expr *exthdr = nft_exthdr(expr);
 
-	if (nla_put_u32(msg, NFTA_EXTHDR_DREG, htonl(exthdr->dreg)) < 0)
-		return -1;
-	if (nla_put_u8(msg, NFTA_EXTHDR_TYPE, exthdr->type) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_EXTHDR_LEN, htonl(exthdr->len)) < 0)
-		return -1;
-
+	if (nfnl_nft_exthdr_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
+	if (nfnl_nft_exthdr_test_type(expr))
+		NLA_PUT_U8(msg, NFTA_EXTHDR_TYPE, exthdr->type);
+	if (nfnl_nft_exthdr_test_offset(expr))
+		NLA_PUT_U32(msg, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
+	if (nfnl_nft_exthdr_test_len(expr))
+		NLA_PUT_U32(msg, NFTA_EXTHDR_LEN, htonl(exthdr->len));
 	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static void nft_exthdr_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
@@ -109,10 +118,29 @@ int nfnl_nft_exthdr_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_exthdr_attrs[] = {
+	__ADD(EXTHDR_ATTR_DREG,			dreg)
+	__ADD(EXTHDR_ATTR_TYPE,			type)
+	__ADD(EXTHDR_ATTR_OFFSET,		offset)
+	__ADD(EXTHDR_ATTR_LEN,			len)
+};
+
+static char *nft_exthdr_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_exthdr_attrs,
+			   ARRAY_SIZE(nft_exthdr_attrs));
+}
+
 void nfnl_nft_exthdr_set_dreg(struct nfnl_nft_expr *expr,
 			       enum nft_registers reg)
 {
 	nft_exthdr(expr)->dreg = reg;
+	expr->ce_mask |= EXTHDR_ATTR_DREG;
+}
+
+int nfnl_nft_exthdr_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & EXTHDR_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_exthdr_get_dreg(const struct nfnl_nft_expr *expr)
@@ -123,6 +151,12 @@ enum nft_registers nfnl_nft_exthdr_get_dreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_exthdr_set_type(struct nfnl_nft_expr *expr, uint8_t type)
 {
 	nft_exthdr(expr)->type = type;
+	expr->ce_mask |= EXTHDR_ATTR_TYPE;
+}
+
+int nfnl_nft_exthdr_test_type(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & EXTHDR_ATTR_TYPE);
 }
 
 uint8_t nfnl_nft_exthdr_get_type(const struct nfnl_nft_expr *expr)
@@ -133,6 +167,12 @@ uint8_t nfnl_nft_exthdr_get_type(const struct nfnl_nft_expr *expr)
 void nfnl_nft_exthdr_set_offset(struct nfnl_nft_expr *expr, unsigned int offset)
 {
 	nft_exthdr(expr)->offset = offset;
+	expr->ce_mask |= EXTHDR_ATTR_OFFSET;
+}
+
+int nfnl_nft_exthdr_test_offset(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & EXTHDR_ATTR_OFFSET);
 }
 
 unsigned int nfnl_nft_exthdr_get_offset(const struct nfnl_nft_expr *expr)
@@ -143,6 +183,12 @@ unsigned int nfnl_nft_exthdr_get_offset(const struct nfnl_nft_expr *expr)
 void nfnl_nft_exthdr_set_len(struct nfnl_nft_expr *expr, unsigned int len)
 {
 	nft_exthdr(expr)->len = len;
+	expr->ce_mask |= EXTHDR_ATTR_LEN;
+}
+
+int nfnl_nft_exthdr_test_len(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & EXTHDR_ATTR_LEN);
 }
 
 unsigned int nfnl_nft_exthdr_get_len(const struct nfnl_nft_expr *expr)
@@ -151,12 +197,13 @@ unsigned int nfnl_nft_exthdr_get_len(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops exthdr_expr_ops = {
-	.eo_name		= "exthdr",
+	.eo_name			= "exthdr",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_exthdr_dump,
-	.eo_get_opts		= nft_exthdr_get_opts,
-	.eo_msg_parser		= nft_exthdr_msg_parser,
-	.eo_policy		= nft_exthdr_policy,
-	.eo_maxattr		= NFTA_EXTHDR_MAX,
+	.eo_get_opts			= nft_exthdr_get_opts,
+	.eo_msg_parser			= nft_exthdr_msg_parser,
+	.eo_attrs2str			= nft_exthdr_attrs2str,
+	.eo_policy			= nft_exthdr_policy,
+	.eo_maxattr			= NFTA_EXTHDR_MAX,
 };
 
 static void __init nft_exthdr_expr_init(void)
diff --git a/lib/netfilter/nft_immediate_expr.c b/lib/netfilter/nft_immediate_expr.c
index 32e13b3..19339dc 100644
--- a/lib/netfilter/nft_immediate_expr.c
+++ b/lib/netfilter/nft_immediate_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -22,6 +22,11 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define IMMEDIATE_ATTR_DREG	(1UL << 0)
+#define IMMEDIATE_ATTR_DATA	(1UL << 1)
+/** @endcond */
+
 struct nft_immediate_expr {
 	struct nfnl_nft_data *	data;
 	enum nft_registers	dreg;
@@ -64,6 +69,7 @@ static void nft_immediate_free_data(struct nfnl_nft_expr *expr)
 static int nft_immediate_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 {
 	struct nft_immediate_expr *immediate;
+	struct nfnl_nft_data *data;
 	int err;
 
 	immediate = nft_immediate_expr_alloc(expr);
@@ -71,11 +77,12 @@ static int nft_immediate_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *t
 		return -ENOMEM;
 
 	if (tb[NFTA_IMMEDIATE_DREG])
-		immediate->dreg = ntohl(nla_get_u32(tb[NFTA_IMMEDIATE_DREG]));
+		nfnl_nft_immediate_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_IMMEDIATE_DREG])));
 	if (tb[NFTA_IMMEDIATE_DATA]) {
-		err = nfnl_nft_data_parse(tb[NFTA_IMMEDIATE_DATA], &immediate->data);
+		err = nfnl_nft_data_parse(tb[NFTA_IMMEDIATE_DATA], &data);
 		if (err < 0)
 			goto errout;
+		nfnl_nft_immediate_set_data(expr, data);
 	}
 	return 0;
 
@@ -88,9 +95,15 @@ static int nft_immediate_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr
 {
 	struct nft_immediate_expr *immediate = nft_immediate(expr);
 
-	if (nla_put_u32(msg, NFTA_IMMEDIATE_DREG, htonl(immediate->dreg)) < 0)
-		return -1;
-	return nfnl_nft_data_put(msg, NFTA_IMMEDIATE_DATA, immediate->data);
+	if (nfnl_nft_immediate_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_IMMEDIATE_DREG, htonl(immediate->dreg));
+	if (nfnl_nft_immediate_test_data(expr) &&
+	    nfnl_nft_data_put(msg, NFTA_IMMEDIATE_DATA, immediate->data) < 0)
+		goto nla_put_failure;
+	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static void nft_immediate_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
@@ -109,10 +122,27 @@ int nfnl_nft_immediate_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_immediate_attrs[] = {
+	__ADD(IMMEDIATE_ATTR_DREG,		dreg)
+	__ADD(IMMEDIATE_ATTR_DATA,		data)
+};
+
+static char *nft_immediate_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_immediate_attrs,
+			   ARRAY_SIZE(nft_immediate_attrs));
+}
+
 void nfnl_nft_immediate_set_dreg(struct nfnl_nft_expr *expr,
 				 enum nft_registers reg)
 {
 	nft_immediate(expr)->dreg = reg;
+	expr->ce_mask |= IMMEDIATE_ATTR_DREG;
+}
+
+int nfnl_nft_immediate_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & IMMEDIATE_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_immediate_get_dreg(const struct nfnl_nft_expr *expr)
@@ -124,6 +154,12 @@ void nfnl_nft_immediate_set_data(struct nfnl_nft_expr *expr,
 				 struct nfnl_nft_data *data)
 {
 	nft_immediate(expr)->data = data;
+	expr->ce_mask |= IMMEDIATE_ATTR_DATA;
+}
+
+int nfnl_nft_immediate_test_data(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & IMMEDIATE_ATTR_DATA);
 }
 
 struct nfnl_nft_data *nfnl_nft_immediate_get_data(const struct nfnl_nft_expr *expr)
@@ -132,14 +168,15 @@ struct nfnl_nft_data *nfnl_nft_immediate_get_data(const struct nfnl_nft_expr *ex
 }
 
 static struct nft_expr_ops immediate_expr_ops = {
-	.eo_name		= "immediate",
+	.eo_name			= "immediate",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_immediate_dump,
-	.eo_get_opts		= nft_immediate_get_opts,
-	.eo_msg_parser		= nft_immediate_msg_parser,
-	.eo_free_data		= nft_immediate_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_immediate_policy,
-	.eo_maxattr		= NFTA_IMMEDIATE_MAX,
+	.eo_get_opts			= nft_immediate_get_opts,
+	.eo_msg_parser			= nft_immediate_msg_parser,
+	.eo_free_data			= nft_immediate_free_data,
+	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_immediate_attrs2str,
+	.eo_policy			= nft_immediate_policy,
+	.eo_maxattr			= NFTA_IMMEDIATE_MAX,
 };
 
 static void __init nft_immediate_expr_init(void)
diff --git a/lib/netfilter/nft_limit_expr.c b/lib/netfilter/nft_limit_expr.c
index a9a43b1..cd49d60 100644
--- a/lib/netfilter/nft_limit_expr.c
+++ b/lib/netfilter/nft_limit_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -43,6 +43,11 @@ static uint64_t htonll(uint64_t x)
 }
 #endif
 
+/** @cond SKIP */
+#define LIMIT_ATTR_RATE		(1UL << 0)
+#define LIMIT_ATTR_DEPTH	(1UL << 1)
+/** @endcond */
+
 struct nft_limit_expr {
 	uint64_t	rate;
 	uint64_t	depth;
@@ -84,10 +89,9 @@ static int nft_limit_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 		return -ENOMEM;
 
 	if (tb[NFTA_LIMIT_RATE])
-		limit->rate = ntohll(nla_get_u64(tb[NFTA_LIMIT_RATE]));
+		nfnl_nft_limit_set_rate(expr, ntohll(nla_get_u64(tb[NFTA_LIMIT_RATE])));
 	if (tb[NFTA_LIMIT_DEPTH])
-		limit->depth = ntohll(nla_get_u64(tb[NFTA_LIMIT_DEPTH]));
-
+		nfnl_nft_limit_set_depth(expr, ntohll(nla_get_u64(tb[NFTA_LIMIT_DEPTH])));
 	return 0;
 }
 
@@ -95,18 +99,13 @@ static int nft_limit_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_limit_expr *limit = nft_limit(expr);
 
-
-	if (limit->rate &&
-	    nla_put_u64(msg, NFTA_LIMIT_RATE, htonll(limit->rate)) < 0)
-		goto errout;
-
-	if (limit->depth &&
-	    nla_put_u64(msg, NFTA_LIMIT_DEPTH, htonll(limit->depth)) < 0)
-		goto errout;
-
+	if (nfnl_nft_limit_test_rate(expr))
+		NLA_PUT_U64(msg, NFTA_LIMIT_RATE, htonll(limit->rate));
+	if (nfnl_nft_limit_test_depth(expr))
+		NLA_PUT_U64(msg, NFTA_LIMIT_DEPTH, htonll(limit->depth));
 	return 0;
 
-errout:
+nla_put_failure:
 	return -1;
 }
 
@@ -115,9 +114,9 @@ static void nft_limit_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
 	struct nft_limit_expr *limit = nft_limit(expr);
 
 	nl_dump(p, "limit");
-	if (limit->rate)
+	if (nfnl_nft_limit_test_rate(expr))
 		nl_dump(p, " rate %llu", (unsigned long long)limit->rate);
-	if (limit->depth)
+	if (nfnl_nft_limit_test_depth(expr))
 		nl_dump(p, " depth %llu", (unsigned long long)limit->depth);
 }
 
@@ -131,9 +130,26 @@ int nfnl_nft_limit_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_limit_attrs[] = {
+	__ADD(LIMIT_ATTR_RATE,			rate)
+	__ADD(LIMIT_ATTR_DEPTH,			depth)
+};
+
+static char *nft_limit_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_limit_attrs,
+			   ARRAY_SIZE(nft_limit_attrs));
+}
+
 void nfnl_nft_limit_set_rate(struct nfnl_nft_expr *expr, uint64_t rate)
 {
 	nft_limit(expr)->rate = rate;
+	expr->ce_mask |= LIMIT_ATTR_RATE;
+}
+
+int nfnl_nft_limit_test_rate(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LIMIT_ATTR_RATE);
 }
 
 uint64_t nfnl_nft_limit_get_rate(const struct nfnl_nft_expr *expr)
@@ -144,6 +160,12 @@ uint64_t nfnl_nft_limit_get_rate(const struct nfnl_nft_expr *expr)
 void nfnl_nft_limit_set_depth(struct nfnl_nft_expr *expr, uint64_t depth)
 {
 	nft_limit(expr)->depth = depth;
+	expr->ce_mask |= LIMIT_ATTR_DEPTH;
+}
+
+int nfnl_nft_limit_test_depth(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LIMIT_ATTR_DEPTH);
 }
 
 uint64_t nfnl_nft_limit_get_depth(const struct nfnl_nft_expr *expr)
@@ -152,14 +174,13 @@ uint64_t nfnl_nft_limit_get_depth(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops limit_expr_ops = {
-	.eo_name		= "limit",
+	.eo_name			= "limit",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_limit_dump,
-	.eo_get_opts		= nft_limit_get_opts,
-	.eo_msg_parser		= nft_limit_msg_parser,
-	.eo_free_data		= NULL, //nft_limit_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_limit_policy,
-	.eo_maxattr		= NFTA_LIMIT_MAX,
+	.eo_get_opts			= nft_limit_get_opts,
+	.eo_msg_parser			= nft_limit_msg_parser,
+	.eo_attrs2str			= nft_limit_attrs2str,
+	.eo_policy			= nft_limit_policy,
+	.eo_maxattr			= NFTA_LIMIT_MAX,
 };
 
 static void __init nft_limit_expr_init(void)
diff --git a/lib/netfilter/nft_log_expr.c b/lib/netfilter/nft_log_expr.c
index 533d8b8..1906118 100644
--- a/lib/netfilter/nft_log_expr.c
+++ b/lib/netfilter/nft_log_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,13 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define LOG_ATTR_PREFIX		(1UL << 0)
+#define LOG_ATTR_GROUP		(1UL << 1)
+#define LOG_ATTR_SNAPLEN	(1UL << 2)
+#define LOG_ATTR_QTHRESHOLD	(1UL << 3)
+/** @endcond */
+
 struct nft_log_expr {
 	char		*prefix;
 	unsigned int	group;
@@ -73,14 +80,13 @@ static int nft_log_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 		return -ENOMEM;
 
 	if (tb[NFTA_LOG_PREFIX])
-		log->prefix = nla_strdup(tb[NFTA_LOG_PREFIX]);
+		nfnl_nft_log_set_prefix(expr, nla_strdup(tb[NFTA_LOG_PREFIX]));
 	if (tb[NFTA_LOG_GROUP])
-		log->group = ntohl(nla_get_u32(tb[NFTA_LOG_GROUP]));
+		nfnl_nft_log_set_group(expr, ntohl(nla_get_u32(tb[NFTA_LOG_GROUP])));
 	if (tb[NFTA_LOG_SNAPLEN])
-		log->snaplen = ntohl(nla_get_u32(tb[NFTA_LOG_SNAPLEN]));
+		nfnl_nft_log_set_snaplen(expr, ntohl(nla_get_u32(tb[NFTA_LOG_SNAPLEN])));
 	if (tb[NFTA_LOG_QTHRESHOLD])
-		log->qthreshold = ntohl(nla_get_u32(tb[NFTA_LOG_QTHRESHOLD]));
-
+		nfnl_nft_log_set_qthreshold(expr, ntohl(nla_get_u32(tb[NFTA_LOG_QTHRESHOLD])));
 	return 0;
 }
 
@@ -88,25 +94,17 @@ static int nft_log_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_log_expr *log = nft_log(expr);
 
-	if (log->prefix != NULL &&
-	    nla_put_string(msg, NFTA_LOG_PREFIX, log->prefix) < 0)
-		goto errout;
-
-	if (log->group &&
-	    nla_put_u32(msg, NFTA_LOG_GROUP, htonl(log->group)) < 0)
-		goto errout;
-
-	if (log->snaplen &&
-	    nla_put_u32(msg, NFTA_LOG_SNAPLEN, htonl(log->snaplen)) < 0)
-		goto errout;
-
-	if (log->qthreshold &&
-	    nla_put_u32(msg, NFTA_LOG_QTHRESHOLD, htonl(log->qthreshold)) < 0)
-		goto errout;
-
+	if (nfnl_nft_log_test_prefix(expr))
+		NLA_PUT_STRING(msg, NFTA_LOG_PREFIX, log->prefix);
+	if (nfnl_nft_log_test_group(expr))
+		NLA_PUT_U32(msg, NFTA_LOG_GROUP, htonl(log->group));
+	if (nfnl_nft_log_test_snaplen(expr))
+		NLA_PUT_U32(msg, NFTA_LOG_SNAPLEN, htonl(log->snaplen));
+	if (nfnl_nft_log_test_qthreshold(expr))
+		NLA_PUT_U32(msg, NFTA_LOG_QTHRESHOLD, htonl(log->qthreshold));
 	return 0;
 
-errout:
+nla_put_failure:
 	return -1;
 }
 
@@ -114,13 +112,13 @@ static void nft_log_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
 {
 	struct nft_log_expr *log = nft_log(expr);
 
-	if (log->prefix)
+	if (nfnl_nft_log_test_prefix(expr))
 		nl_dump(p, " prefix '%s'", log->prefix);
-	if (log->group)
+	if (nfnl_nft_log_test_group(expr))
 		nl_dump(p, " group %u", log->group);
-	if (log->snaplen)
+	if (nfnl_nft_log_test_snaplen(expr))
 		nl_dump(p, " snaplen %u", log->snaplen);
-	if (log->qthreshold)
+	if (nfnl_nft_log_test_qthreshold(expr))
 		nl_dump(p, " qthreshold %u", log->qthreshold);
 }
 
@@ -134,9 +132,28 @@ int nfnl_nft_log_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_log_attrs[] = {
+	__ADD(LOG_ATTR_PREFIX,			prefix)
+	__ADD(LOG_ATTR_GROUP,			group)
+	__ADD(LOG_ATTR_SNAPLEN,			snaplen)
+	__ADD(LOG_ATTR_QTHRESHOLD,		qthreshold)
+};
+
+static char *nft_log_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_log_attrs,
+			   ARRAY_SIZE(nft_log_attrs));
+}
+
 void nfnl_nft_log_set_prefix(struct nfnl_nft_expr *expr, const char *prefix)
 {
 	nft_log(expr)->prefix = strdup(prefix);
+	expr->ce_mask |= LOG_ATTR_PREFIX;
+}
+
+int nfnl_nft_log_test_prefix(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOG_ATTR_PREFIX);
 }
 
 const char *nfnl_nft_log_get_prefix(const struct nfnl_nft_expr *expr)
@@ -147,6 +164,12 @@ const char *nfnl_nft_log_get_prefix(const struct nfnl_nft_expr *expr)
 void nfnl_nft_log_set_group(struct nfnl_nft_expr *expr, uint32_t group)
 {
 	nft_log(expr)->group = group;
+	expr->ce_mask |= LOG_ATTR_GROUP;
+}
+
+int nfnl_nft_log_test_group(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOG_ATTR_GROUP);
 }
 
 uint32_t nfnl_nft_log_get_group(const struct nfnl_nft_expr *expr)
@@ -157,6 +180,12 @@ uint32_t nfnl_nft_log_get_group(const struct nfnl_nft_expr *expr)
 void nfnl_nft_log_set_snaplen(struct nfnl_nft_expr *expr, uint32_t snaplen)
 {
 	nft_log(expr)->snaplen = snaplen;
+	expr->ce_mask |= LOG_ATTR_SNAPLEN;
+}
+
+int nfnl_nft_log_test_snaplen(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOG_ATTR_SNAPLEN);
 }
 
 uint32_t nfnl_nft_log_get_snaplen(const struct nfnl_nft_expr *expr)
@@ -167,6 +196,12 @@ uint32_t nfnl_nft_log_get_snaplen(const struct nfnl_nft_expr *expr)
 void nfnl_nft_log_set_qthreshold(struct nfnl_nft_expr *expr, uint32_t qthreshold)
 {
 	nft_log(expr)->qthreshold = qthreshold;
+	expr->ce_mask |= LOG_ATTR_QTHRESHOLD;
+}
+
+int nfnl_nft_log_test_qthreshold(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOG_ATTR_QTHRESHOLD);
 }
 
 uint32_t nfnl_nft_log_get_qthreshold(const struct nfnl_nft_expr *expr)
@@ -175,14 +210,15 @@ uint32_t nfnl_nft_log_get_qthreshold(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops log_expr_ops = {
-	.eo_name		= "log",
+	.eo_name			= "log",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_log_dump,
-	.eo_get_opts		= nft_log_get_opts,
-	.eo_msg_parser		= nft_log_msg_parser,
-	.eo_free_data		= nft_log_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_log_policy,
-	.eo_maxattr		= NFTA_LOG_MAX,
+	.eo_get_opts			= nft_log_get_opts,
+	.eo_msg_parser			= nft_log_msg_parser,
+	.eo_free_data			= nft_log_free_data,
+	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_log_attrs2str,
+	.eo_policy			= nft_log_policy,
+	.eo_maxattr			= NFTA_LOG_MAX,
 };
 
 static void __init nft_log_expr_init(void)
diff --git a/lib/netfilter/nft_lookup_expr.c b/lib/netfilter/nft_lookup_expr.c
index 45931dd..6e4d507 100644
--- a/lib/netfilter/nft_lookup_expr.c
+++ b/lib/netfilter/nft_lookup_expr.c
@@ -22,6 +22,12 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define LOOKUP_ATTR_SET		(1UL << 0)
+#define LOOKUP_ATTR_SREG	(1UL << 1)
+#define LOOKUP_ATTR_DREG	(1UL << 2)
+/** @endcond */
+
 struct nft_lookup_expr {
 	char			*set;
 	enum nft_registers	sreg;
@@ -67,20 +73,23 @@ static void nft_lookup_free_data(struct nfnl_nft_expr *expr)
 static int nft_lookup_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 {
 	struct nft_lookup_expr *lookup;
+	char *set;
 
 	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)
+		set = nla_strdup(tb[NFTA_LOOKUP_SET]);
+		if (set == NULL)
 			goto err2;
+		nfnl_nft_lookup_set_set(expr, set);
+		free(set);
 	}
 	if (tb[NFTA_LOOKUP_SREG] != NULL)
-		lookup->sreg = ntohl(nla_get_u32(tb[NFTA_LOOKUP_SREG]));
+		nfnl_nft_lookup_set_sreg(expr, ntohl(nla_get_u32(tb[NFTA_LOOKUP_SREG])));
 	if (tb[NFTA_LOOKUP_DREG] != NULL)
-		lookup->dreg = ntohl(nla_get_u32(tb[NFTA_LOOKUP_DREG]));
+		nfnl_nft_lookup_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_LOOKUP_DREG])));
 
 	return 0;
 
@@ -94,17 +103,12 @@ 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)
+	if (nfnl_nft_lookup_test_sreg(expr))
+		NLA_PUT_U32(msg, NFTA_LOOKUP_SREG, htonl(lookup->sreg));
+	if (nfnl_nft_lookup_test_set(expr))
 		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
+	if (nfnl_nft_lookup_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_LOOKUP_DREG, htonl(lookup->dreg));
 	return 0;
 
 nla_put_failure:
@@ -116,6 +120,8 @@ 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);
+	if (expr->ce_mask & LOOKUP_ATTR_DREG)
+		nl_dump(p, " dreg %u", lookup->dreg);
 }
 
 int nfnl_nft_lookup_init(struct nfnl_nft_expr *expr)
@@ -125,9 +131,27 @@ int nfnl_nft_lookup_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_lookup_attrs[] = {
+	__ADD(LOOKUP_ATTR_SET,			set)
+	__ADD(LOOKUP_ATTR_SREG,			sreg)
+	__ADD(LOOKUP_ATTR_DREG,			dreg)
+};
+
+static char *nft_lookup_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_lookup_attrs,
+			   ARRAY_SIZE(nft_lookup_attrs));
+}
+
 void nfnl_nft_lookup_set_set(struct nfnl_nft_expr *expr, const char *set)
 {
 	nft_lookup(expr)->set = strdup(set);
+	expr->ce_mask |= LOOKUP_ATTR_SET;
+}
+
+int nfnl_nft_lookup_test_set(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOOKUP_ATTR_SET);
 }
 
 const char *nfnl_nft_lookup_get_set(const struct nfnl_nft_expr *expr)
@@ -138,6 +162,12 @@ const char *nfnl_nft_lookup_get_set(const struct nfnl_nft_expr *expr)
 void nfnl_nft_lookup_set_sreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_lookup(expr)->sreg = reg;
+	expr->ce_mask |= LOOKUP_ATTR_SREG;
+}
+
+int nfnl_nft_lookup_test_sreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOOKUP_ATTR_SREG);
 }
 
 enum nft_registers nfnl_nft_lookup_get_sreg(const struct nfnl_nft_expr *expr)
@@ -148,6 +178,12 @@ enum nft_registers nfnl_nft_lookup_get_sreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_lookup_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_lookup(expr)->dreg = reg;
+	expr->ce_mask |= LOOKUP_ATTR_DREG;
+}
+
+int nfnl_nft_lookup_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & LOOKUP_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_lookup_get_dreg(const struct nfnl_nft_expr *expr)
@@ -162,6 +198,7 @@ static struct nft_expr_ops lookup_expr_ops = {
 	.eo_msg_parser			= nft_lookup_msg_parser,
 	.eo_free_data			= nft_lookup_free_data,
 	.eo_clone			= NULL,
+	.eo_attrs2str			= nft_lookup_attrs2str,
 	.eo_policy			= nft_lookup_policy,
 	.eo_maxattr			= NFTA_LOOKUP_MAX,
 };
diff --git a/lib/netfilter/nft_meta_expr.c b/lib/netfilter/nft_meta_expr.c
index faf17db..9c81503 100644
--- a/lib/netfilter/nft_meta_expr.c
+++ b/lib/netfilter/nft_meta_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,11 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define META_ATTR_DREG		(1UL << 0)
+#define META_ATTR_KEY		(1UL << 1)
+/** @endcond */
+
 struct nft_meta_expr {
 	enum nft_meta_keys	key;
 	enum nft_registers	dreg;
@@ -62,10 +67,9 @@ static int nft_meta_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 		return -ENOMEM;
 
 	if (tb[NFTA_META_DREG])
-		meta->dreg = ntohl(nla_get_u32(tb[NFTA_META_DREG]));
+		nfnl_nft_meta_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_META_DREG])));
 	if (tb[NFTA_META_KEY])
-		meta->key = ntohl(nla_get_u32(tb[NFTA_META_KEY]));
-
+		nfnl_nft_meta_set_key(expr, ntohl(nla_get_u32(tb[NFTA_META_KEY])));
 	return 0;
 }
 
@@ -73,11 +77,14 @@ static int nft_meta_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_meta_expr *meta = nft_meta(expr);
 
-	if (nla_put_u32(msg, NFTA_META_DREG, htonl(meta->dreg)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_META_KEY, htonl(meta->key)) < 0)
-		return -1;
+	if (nfnl_nft_meta_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_META_DREG, htonl(meta->dreg));
+	if (nfnl_nft_meta_test_key(expr))
+		NLA_PUT_U32(msg, NFTA_META_KEY, htonl(meta->key));
 	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static struct trans_tbl meta_keys[] = {
@@ -125,9 +132,26 @@ int nfnl_nft_meta_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_meta_attrs[] = {
+	__ADD(META_ATTR_DREG,			dreg)
+	__ADD(META_ATTR_KEY,			key)
+};
+
+static char *nft_meta_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_meta_attrs,
+			   ARRAY_SIZE(nft_meta_attrs));
+}
+
 void nfnl_nft_meta_set_dreg(struct nfnl_nft_expr *expr, enum nft_registers reg)
 {
 	nft_meta(expr)->dreg = reg;
+	expr->ce_mask |= META_ATTR_DREG;
+}
+
+int nfnl_nft_meta_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & META_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_meta_get_dreg(const struct nfnl_nft_expr *expr)
@@ -138,6 +162,12 @@ enum nft_registers nfnl_nft_meta_get_dreg(const struct nfnl_nft_expr *expr)
 void nfnl_nft_meta_set_key(struct nfnl_nft_expr *expr, enum nft_meta_keys key)
 {
 	nft_meta(expr)->key = key;
+	expr->ce_mask |= META_ATTR_KEY;
+}
+
+int nfnl_nft_meta_test_key(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & META_ATTR_KEY);
 }
 
 enum nft_meta_keys nfnl_nft_meta_get_key(const struct nfnl_nft_expr *expr)
@@ -146,12 +176,13 @@ enum nft_meta_keys nfnl_nft_meta_get_key(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops meta_expr_ops = {
-	.eo_name		= "meta",
+	.eo_name			= "meta",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_meta_dump,
-	.eo_get_opts		= nft_meta_get_opts,
-	.eo_msg_parser		= nft_meta_msg_parser,
-	.eo_policy		= nft_meta_policy,
-	.eo_maxattr		= NFTA_SET_MAX,
+	.eo_get_opts			= nft_meta_get_opts,
+	.eo_msg_parser			= nft_meta_msg_parser,
+	.eo_attrs2str			= nft_meta_attrs2str,
+	.eo_policy			= nft_meta_policy,
+	.eo_maxattr			= NFTA_SET_MAX,
 };
 
 static void __init nft_meta_expr_init(void)
diff --git a/lib/netfilter/nft_nat_expr.c b/lib/netfilter/nft_nat_expr.c
index bb6a410..8d92d12 100644
--- a/lib/netfilter/nft_nat_expr.c
+++ b/lib/netfilter/nft_nat_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,14 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define NAT_ATTR_TYPE		(1UL << 0)
+#define NAT_ATTR_SREG_ADDR_MIN	(1UL << 1)
+#define NAT_ATTR_SREG_ADDR_MAX	(1UL << 2)
+#define NAT_ATTR_SREG_PROTO_MIN	(1UL << 3)
+#define NAT_ATTR_SREG_PROTO_MAX	(1UL << 4)
+/** @endcond */
+
 struct nft_nat_expr {
 	enum nft_nat_types	type;
 	enum nft_registers	sreg_addr_min;
@@ -68,17 +76,15 @@ static int nft_nat_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[])
 		return -ENOMEM;
 
 	if (tb[NFTA_NAT_TYPE])
-		nat->type = ntohl(nla_get_u32(tb[NFTA_NAT_TYPE]));
-
+		nfnl_nft_nat_set_type(expr, ntohl(nla_get_u32(tb[NFTA_NAT_TYPE])));
 	if (tb[NFTA_NAT_ADDR_MIN])
-		nat->sreg_addr_min = ntohl(nla_get_u32(tb[NFTA_NAT_ADDR_MIN]));
+		nfnl_nft_nat_set_sreg_addr_min(expr, ntohl(nla_get_u32(tb[NFTA_NAT_ADDR_MIN])));
 	if (tb[NFTA_NAT_ADDR_MAX])
-		nat->sreg_addr_max = ntohl(nla_get_u32(tb[NFTA_NAT_ADDR_MAX]));
+		nfnl_nft_nat_set_sreg_addr_max(expr, ntohl(nla_get_u32(tb[NFTA_NAT_ADDR_MAX])));
 	if (tb[NFTA_NAT_PROTO_MIN])
-		nat->sreg_proto_min = ntohl(nla_get_u32(tb[NFTA_NAT_PROTO_MIN]));
+		nfnl_nft_nat_set_sreg_proto_min(expr, ntohl(nla_get_u32(tb[NFTA_NAT_PROTO_MIN])));
 	if (tb[NFTA_NAT_PROTO_MAX])
-		nat->sreg_proto_max = ntohl(nla_get_u32(tb[NFTA_NAT_PROTO_MAX]));
-
+		nfnl_nft_nat_set_sreg_proto_max(expr, ntohl(nla_get_u32(tb[NFTA_NAT_PROTO_MAX])));
 	return 0;
 }
 
@@ -86,23 +92,20 @@ static int nft_nat_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_nat_expr *nat = nft_nat(expr);
 
-	if (nla_put_u32(msg, NFTA_NAT_TYPE, htonl(nat->type)) < 0)
-		return -1;
-
-	if (nat->sreg_addr_min &&
-	    nla_put_u32(msg, NFTA_NAT_ADDR_MIN, htonl(nat->sreg_addr_min)) < 0)
-		return -1;
-	if (nat->sreg_addr_max &&
-	    nla_put_u32(msg, NFTA_NAT_ADDR_MAX, htonl(nat->sreg_addr_max)) < 0)
-		return -1;
-	if (nat->sreg_proto_min &&
-	    nla_put_u32(msg, NFTA_NAT_PROTO_MIN, htonl(nat->sreg_proto_min)) < 0)
-		return -1;
-	if (nat->sreg_proto_max &&
-	    nla_put_u32(msg, NFTA_NAT_PROTO_MAX, htonl(nat->sreg_proto_max)) < 0)
-		return -1;
-
+	if (nfnl_nft_nat_test_type(expr))
+		NLA_PUT_U32(msg, NFTA_NAT_TYPE, htonl(nat->type));
+	if (nfnl_nft_nat_test_sreg_addr_min(expr))
+		NLA_PUT_U32(msg, NFTA_NAT_ADDR_MIN, htonl(nat->sreg_addr_min));
+	if (nfnl_nft_nat_test_sreg_addr_max(expr))
+		NLA_PUT_U32(msg, NFTA_NAT_ADDR_MAX, htonl(nat->sreg_addr_max));
+	if (nfnl_nft_nat_test_sreg_proto_min(expr))
+		NLA_PUT_U32(msg, NFTA_NAT_PROTO_MIN, htonl(nat->sreg_proto_min));
+	if (nfnl_nft_nat_test_sreg_proto_min(expr))
+		NLA_PUT_U32(msg, NFTA_NAT_PROTO_MAX, htonl(nat->sreg_proto_max));
 	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static void nft_nat_dump(struct nfnl_nft_expr *expr, struct nl_dump_params *p)
@@ -134,9 +137,29 @@ int nfnl_nft_nat_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_nat_attrs[] = {
+	__ADD(NAT_ATTR_TYPE,			type)
+	__ADD(NAT_ATTR_SREG_ADDR_MIN,		sreg_addr_min)
+	__ADD(NAT_ATTR_SREG_ADDR_MAX,		sreg_addr_max)
+	__ADD(NAT_ATTR_SREG_PROTO_MIN,		sreg_proto_min)
+	__ADD(NAT_ATTR_SREG_PROTO_MAX,		sreg_proto_max)
+};
+
+static char *nft_nat_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_nat_attrs,
+			   ARRAY_SIZE(nft_nat_attrs));
+}
+
 void nfnl_nft_nat_set_type(struct nfnl_nft_expr *expr, enum nft_nat_types type)
 {
 	nft_nat(expr)->type = type;
+	expr->ce_mask |= NAT_ATTR_TYPE;
+}
+
+int nfnl_nft_nat_test_type(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & NAT_ATTR_TYPE);
 }
 
 enum nft_nat_types nfnl_nft_nat_get_type(const struct nfnl_nft_expr *expr)
@@ -148,6 +171,12 @@ void nfnl_nft_nat_set_sreg_addr_min(struct nfnl_nft_expr *expr,
 				    enum nft_registers sreg)
 {
 	nft_nat(expr)->sreg_addr_min = sreg;
+	expr->ce_mask |= NAT_ATTR_SREG_ADDR_MIN;
+}
+
+int nfnl_nft_nat_test_sreg_addr_min(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & NAT_ATTR_SREG_ADDR_MIN);
 }
 
 enum nft_registers nfnl_nft_nat_get_sreg_addr_min(const struct nfnl_nft_expr *expr)
@@ -159,6 +188,12 @@ void nfnl_nft_nat_set_sreg_addr_max(struct nfnl_nft_expr *expr,
 				    enum nft_registers sreg)
 {
 	nft_nat(expr)->sreg_addr_max = sreg;
+	expr->ce_mask |= NAT_ATTR_SREG_ADDR_MAX;
+}
+
+int nfnl_nft_nat_test_sreg_addr_max(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & NAT_ATTR_SREG_ADDR_MAX);
 }
 
 enum nft_registers nfnl_nft_nat_get_sreg_addr_max(const struct nfnl_nft_expr *expr)
@@ -170,6 +205,12 @@ void nfnl_nft_nat_set_sreg_proto_min(struct nfnl_nft_expr *expr,
 				     enum nft_registers sreg)
 {
 	nft_nat(expr)->sreg_proto_min = sreg;
+	expr->ce_mask |= NAT_ATTR_SREG_PROTO_MIN;
+}
+
+int nfnl_nft_nat_test_sreg_proto_min(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & NAT_ATTR_SREG_PROTO_MIN);
 }
 
 enum nft_registers nfnl_nft_nat_get_sreg_proto_min(const struct nfnl_nft_expr *expr)
@@ -181,6 +222,12 @@ void nfnl_nft_nat_set_sreg_proto_max(struct nfnl_nft_expr *expr,
 				     enum nft_registers sreg)
 {
 	nft_nat(expr)->sreg_proto_max = sreg;
+	expr->ce_mask |= NAT_ATTR_SREG_PROTO_MAX;
+}
+
+int nfnl_nft_nat_test_sreg_proto_max(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & NAT_ATTR_SREG_PROTO_MAX);
 }
 
 enum nft_registers nfnl_nft_nat_get_sreg_proto_max(const struct nfnl_nft_expr *expr)
@@ -189,14 +236,13 @@ enum nft_registers nfnl_nft_nat_get_sreg_proto_max(const struct nfnl_nft_expr *e
 }
 
 static struct nft_expr_ops nat_expr_ops = {
-	.eo_name		= "nat",
+	.eo_name			= "nat",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_nat_dump,
-	.eo_get_opts		= nft_nat_get_opts,
-	.eo_msg_parser		= nft_nat_msg_parser,
-	.eo_free_data		= NULL, //nft_nat_free_data,
-	.eo_clone		= NULL,
-	.eo_policy		= nft_nat_policy,
-	.eo_maxattr		= NFTA_NAT_MAX,
+	.eo_get_opts			= nft_nat_get_opts,
+	.eo_msg_parser			= nft_nat_msg_parser,
+	.eo_attrs2str			= nft_nat_attrs2str,
+	.eo_policy			= nft_nat_policy,
+	.eo_maxattr			= NFTA_NAT_MAX,
 };
 
 static void __init nft_nat_expr_init(void)
diff --git a/lib/netfilter/nft_payload_expr.c b/lib/netfilter/nft_payload_expr.c
index 3154ba6..e4c2cd8 100644
--- a/lib/netfilter/nft_payload_expr.c
+++ b/lib/netfilter/nft_payload_expr.c
@@ -6,7 +6,7 @@
  *	License as published by the Free Software Foundation version 2.1
  *	of the License.
  *
- * Copyright (c) 2008 Patrick McHardy <kaber at trash.net>
+ * Copyright (c) 2008-2009 Patrick McHardy <kaber at trash.net>
  *
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
@@ -21,6 +21,13 @@
 #include <netlink/netfilter/nft_expr.h>
 #include <netlink/netfilter/nft-expr-modules.h>
 
+/** @cond SKIP */
+#define PAYLOAD_ATTR_DREG	(1UL << 0)
+#define PAYLOAD_ATTR_BASE	(1UL << 1)
+#define PAYLOAD_ATTR_OFFSET	(1UL << 2)
+#define PAYLOAD_ATTR_LEN	(1UL << 3)
+/** @endcond */
+
 struct nft_payload_expr {
 	enum nft_registers	dreg;
 	enum nft_payload_bases	base;
@@ -66,14 +73,13 @@ static int nft_payload_msg_parser(struct nfnl_nft_expr *expr, struct nlattr *tb[
 		return -ENOMEM;
 
 	if (tb[NFTA_PAYLOAD_DREG])
-		payload->dreg = ntohl(nla_get_u32(tb[NFTA_PAYLOAD_DREG]));
+		nfnl_nft_payload_set_dreg(expr, ntohl(nla_get_u32(tb[NFTA_PAYLOAD_DREG])));
 	if (tb[NFTA_PAYLOAD_BASE])
-		payload->base = ntohl(nla_get_u32(tb[NFTA_PAYLOAD_BASE]));
+		nfnl_nft_payload_set_base(expr, ntohl(nla_get_u32(tb[NFTA_PAYLOAD_BASE])));
 	if (tb[NFTA_PAYLOAD_OFFSET])
-		payload->offset = ntohl(nla_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
+		nfnl_nft_payload_set_offset(expr, ntohl(nla_get_u32(tb[NFTA_PAYLOAD_OFFSET])));
 	if (tb[NFTA_PAYLOAD_LEN])
-		payload->len = ntohl(nla_get_u32(tb[NFTA_PAYLOAD_LEN]));
-
+		nfnl_nft_payload_set_len(expr, ntohl(nla_get_u32(tb[NFTA_PAYLOAD_LEN])));
 	return 0;
 }
 
@@ -81,16 +87,18 @@ static int nft_payload_get_opts(struct nl_msg *msg, struct nfnl_nft_expr *expr)
 {
 	struct nft_payload_expr *payload = nft_payload(expr);
 
-	if (nla_put_u32(msg, NFTA_PAYLOAD_DREG, htonl(payload->dreg)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_PAYLOAD_BASE, htonl(payload->base)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_PAYLOAD_OFFSET, htonl(payload->offset)) < 0)
-		return -1;
-	if (nla_put_u32(msg, NFTA_PAYLOAD_LEN, htonl(payload->len)) < 0)
-		return -1;
-
+	if (nfnl_nft_payload_test_dreg(expr))
+		NLA_PUT_U32(msg, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
+	if (nfnl_nft_payload_test_base(expr))
+		NLA_PUT_U32(msg, NFTA_PAYLOAD_BASE, htonl(payload->base));
+	if (nfnl_nft_payload_test_offset(expr))
+		NLA_PUT_U32(msg, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
+	if (nfnl_nft_payload_test_len(expr))
+		NLA_PUT_U32(msg, NFTA_PAYLOAD_LEN, htonl(payload->len));
 	return 0;
+
+nla_put_failure:
+	return -1;
 }
 
 static const char *nft_payload_base_names[] = {
@@ -115,10 +123,29 @@ int nfnl_nft_payload_init(struct nfnl_nft_expr *expr)
 	return 0;
 }
 
+static struct trans_tbl nft_payload_attrs[] = {
+	__ADD(PAYLOAD_ATTR_DREG,		dreg)
+	__ADD(PAYLOAD_ATTR_BASE,		base)
+	__ADD(PAYLOAD_ATTR_OFFSET,		offset)
+	__ADD(PAYLOAD_ATTR_LEN,			len)
+};
+
+static char *nft_payload_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, nft_payload_attrs,
+			   ARRAY_SIZE(nft_payload_attrs));
+}
+
 void nfnl_nft_payload_set_dreg(struct nfnl_nft_expr *expr,
 			       enum nft_registers reg)
 {
 	nft_payload(expr)->dreg = reg;
+	expr->ce_mask |= PAYLOAD_ATTR_DREG;
+}
+
+int nfnl_nft_payload_test_dreg(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & PAYLOAD_ATTR_DREG);
 }
 
 enum nft_registers nfnl_nft_payload_get_dreg(const struct nfnl_nft_expr *expr)
@@ -130,6 +157,12 @@ void nfnl_nft_payload_set_base(struct nfnl_nft_expr *expr,
 			       enum nft_payload_bases base)
 {
 	nft_payload(expr)->base = base;
+	expr->ce_mask |= PAYLOAD_ATTR_BASE;
+}
+
+int nfnl_nft_payload_test_base(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & PAYLOAD_ATTR_BASE);
 }
 
 enum nft_payload_bases nfnl_nft_payload_get_base(const struct nfnl_nft_expr *expr)
@@ -140,6 +173,12 @@ enum nft_payload_bases nfnl_nft_payload_get_base(const struct nfnl_nft_expr *exp
 void nfnl_nft_payload_set_offset(struct nfnl_nft_expr *expr, unsigned int offset)
 {
 	nft_payload(expr)->offset = offset;
+	expr->ce_mask |= PAYLOAD_ATTR_OFFSET;
+}
+
+int nfnl_nft_payload_test_offset(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & PAYLOAD_ATTR_OFFSET);
 }
 
 unsigned int nfnl_nft_payload_get_offset(const struct nfnl_nft_expr *expr)
@@ -150,6 +189,12 @@ unsigned int nfnl_nft_payload_get_offset(const struct nfnl_nft_expr *expr)
 void nfnl_nft_payload_set_len(struct nfnl_nft_expr *expr, unsigned int len)
 {
 	nft_payload(expr)->len = len;
+	expr->ce_mask |= PAYLOAD_ATTR_LEN;
+}
+
+int nfnl_nft_payload_test_len(const struct nfnl_nft_expr *expr)
+{
+	return !!(expr->ce_mask & PAYLOAD_ATTR_LEN);
 }
 
 unsigned int nfnl_nft_payload_get_len(const struct nfnl_nft_expr *expr)
@@ -158,12 +203,13 @@ unsigned int nfnl_nft_payload_get_len(const struct nfnl_nft_expr *expr)
 }
 
 static struct nft_expr_ops payload_expr_ops = {
-	.eo_name		= "payload",
+	.eo_name			= "payload",
 	.eo_dump[NL_DUMP_DETAILS]	= nft_payload_dump,
-	.eo_get_opts		= nft_payload_get_opts,
-	.eo_msg_parser		= nft_payload_msg_parser,
-	.eo_policy		= nft_payload_policy,
-	.eo_maxattr		= NFTA_PAYLOAD_MAX,
+	.eo_get_opts			= nft_payload_get_opts,
+	.eo_msg_parser			= nft_payload_msg_parser,
+	.eo_attrs2str			= nft_payload_attrs2str,
+	.eo_policy			= nft_payload_policy,
+	.eo_maxattr			= NFTA_PAYLOAD_MAX,
 };
 
 static void __init nft_payload_expr_init(void)



More information about the netfilter-cvslog mailing list