diff --git a/configure.ac b/configure.ac
index 316d043..811d7e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,11 +49,11 @@ then
 fi
 
 # Checks for libraries.
-AC_CHECK_LIB([nl], [nl_socket_alloc], ,
-	     AC_MSG_ERROR([No suitable version of libnl found]))
+AC_CHECK_LIB([mnl], [mnl_socket_open], ,
+	     AC_MSG_ERROR([No suitable version of libmnl found]))
 
-AC_CHECK_LIB([nl-nf], [nfnl_nft_rule_alloc], ,
-	     AC_MSG_ERROR([No suitable version of libnl-nf found]))
+AC_CHECK_LIB([nftables], [nft_rule_alloc], ,
+	     AC_MSG_ERROR([No suitable version of libnftables found]))
 
 AC_CHECK_LIB([gmp], [__gmpz_init], ,
 	     AC_MSG_ERROR([No suitable version of libgmp found]))
diff --git a/include/mnl.h b/include/mnl.h
new file mode 100644
index 0000000..c878ce4
--- /dev/null
+++ b/include/mnl.h
@@ -0,0 +1,43 @@
+#ifndef _MNL_H_
+#define _MNL_H_
+
+int mnl_nft_rule_add(struct mnl_socket *nf_sock, struct nft_rule *r,
+		     unsigned int flags);
+int mnl_nft_rule_delete(struct mnl_socket *nf_sock, struct nft_rule *r,
+			unsigned int flags);
+struct nft_rule_list *mnl_nft_rule_dump(struct mnl_socket *nf_sock,
+					int family);
+
+int mnl_nft_chain_add(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+		      unsigned int flags);
+int mnl_nft_chain_delete(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+                         unsigned int flags);
+struct nft_chain_list *mnl_nft_chain_dump(struct mnl_socket *nf_sock,
+					  int family);
+int mnl_nft_chain_get(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+		      unsigned int flags);
+
+int mnl_nft_table_add(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags);
+int mnl_nft_table_delete(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags);
+struct nft_table_list *mnl_nft_table_dump(struct mnl_socket *nf_sock,
+					  int family);
+int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags);
+
+int mnl_nft_set_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+		    unsigned int flags);
+int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+		       unsigned int flags);
+struct nft_set_list *mnl_nft_set_dump(struct mnl_socket *nf_sock, int family,
+				      const char *table);
+int mnl_nft_set_get(struct mnl_socket *nf_sock, struct nft_set *nls);
+
+int mnl_nft_setelem_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+			unsigned int flags);
+int mnl_nft_setelem_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+			   unsigned int flags);
+int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls);
+
+#endif
diff --git a/include/netlink.h b/include/netlink.h
index ccbb872..19574f0 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -1,13 +1,14 @@
 #ifndef NFTABLES_NETLINK_H
 #define NFTABLES_NETLINK_H
 
-#include <netlink/netfilter/netfilter.h>
-#include <netlink/netfilter/nft_table.h>
-#include <netlink/netfilter/nft_chain.h>
-#include <netlink/netfilter/nft_rule.h>
-#include <netlink/netfilter/nft_expr.h>
-#include <netlink/netfilter/nft_data.h>
-#include <netlink/object.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+#include <libnftables/set.h>
+
+#include <linux/netlink.h>
+#include <linux/netfilter/nf_tables.h>
 
 #include <rule.h>
 
@@ -17,45 +18,57 @@
  * @msgs:	message queue
  * @list:	list of parsed rules/chains/tables
  * @set:	current set
+ * @data:	pointer to pass data to callback
  */
 struct netlink_ctx {
 	struct list_head	*msgs;
 	struct list_head	list;
 	struct set		*set;
+	const void		*data;
 };
 
-extern void netlink_dump_object(struct nl_object *obj);
+extern struct nft_table *alloc_nft_table(const struct handle *h);
+extern struct nft_chain *alloc_nft_chain(const struct handle *h);
+extern struct nft_rule *alloc_nft_rule(const struct handle *h);
+extern struct nft_rule_expr *alloc_nft_expr(const char *name);
+extern struct nft_set *alloc_nft_set(const struct handle *h);
 
-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);
+struct nft_data_linearize {
+	size_t		len;
+	uint32_t	value[4];
+	char		chain[NFT_CHAIN_MAXNAMELEN];
+	int		verdict;
+};
 
-extern struct nfnl_nft_data *netlink_gen_data(const struct expr *expr);
-extern struct nfnl_nft_data *netlink_gen_raw_data(const mpz_t value,
-						  enum byteorder byteorder,
-						  unsigned int len);
+struct nft_data_delinearize {
+	size_t		len;
+	const uint32_t	*value;
+	const char	*chain;
+	int		verdict;
+};
+
+extern void netlink_gen_data(const struct expr *expr,
+			     struct nft_data_linearize *data);
+extern void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder,
+				 unsigned int len,
+				 struct nft_data_linearize *data);
 
 extern struct expr *netlink_alloc_value(const struct location *loc,
-				        const struct nfnl_nft_data *nld);
+				        const struct nft_data_delinearize *nld);
 extern struct expr *netlink_alloc_data(const struct location *loc,
-				       const struct nfnl_nft_data *nld,
+				       const struct nft_data_delinearize *nld,
 				       enum nft_registers dreg);
 
 extern int netlink_linearize_rule(struct netlink_ctx *ctx,
-				  struct nfnl_nft_rule *nlr,
+				  struct nft_rule *nlr,
 				  const struct rule *rule);
 extern struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
-					     const struct nl_object *obj);
+					     const struct nft_rule *r);
 
 extern int netlink_add_rule(struct netlink_ctx *ctx, const struct handle *h,
 			    const struct rule *rule, uint32_t flags);
 extern int netlink_delete_rule(struct netlink_ctx *ctx, const struct handle *h,
 			       const struct location *loc);
-extern int netlink_get_rule(struct netlink_ctx *ctx, const struct handle *h,
-			    const struct location *loc);
 
 extern int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h,
 			     const struct location *loc,
@@ -103,4 +116,10 @@ extern int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle
 extern int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
 				const struct location *loc, struct set *set);
 
+extern void netlink_dump_table(struct nft_table *nlt);
+extern void netlink_dump_chain(struct nft_chain *nlc);
+extern void netlink_dump_rule(struct nft_rule *nlr);
+extern void netlink_dump_expr(struct nft_rule_expr *nle);
+extern void netlink_dump_set(struct nft_set *nls);
+
 #endif /* NFTABLES_NETLINK_H */
diff --git a/include/nftables.h b/include/nftables.h
index ff91d93..ec21d37 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -50,7 +50,7 @@ struct location {
 			unsigned int		last_column;
 		};
 		struct {
-			struct nl_object	*nl_obj;
+			void			*nle;
 		};
 	};
 };
diff --git a/src/Makefile.in b/src/Makefile.in
index 6bfcd64..658e9b3 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -21,6 +21,7 @@ nft-obj			+= rbtree.o
 nft-obj			+= gmputil.o
 nft-obj			+= utils.o
 nft-obj			+= erec.o
+nft-obj			+= mnl.o
 
 nft-obj			+= parser.o
 nft-extra-clean-files	+= parser.c parser.h
diff --git a/src/erec.c b/src/erec.c
index f2b0ce6..7451d94 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -107,7 +107,7 @@ void erec_print(FILE *f, const struct error_record *erec)
 		fprintf(f, "%s\n", erec->msg);
 		for (l = 0; l < (int)erec->num_locations; l++) {
 			loc = &erec->locations[l];
-			netlink_dump_object(loc->nl_obj);
+			netlink_dump_expr(loc->nle);
 		}
 		fprintf(f, "\n");
 	} else {
diff --git a/src/mnl.c b/src/mnl.c
new file mode 100644
index 0000000..ea9637c
--- /dev/null
+++ b/src/mnl.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <libmnl/libmnl.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+#include <libnftables/set.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <mnl.h>
+#include <errno.h>
+#include <utils.h>
+
+static int seq;
+
+static int
+mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
+	 int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	uint32_t portid = mnl_socket_get_portid(nf_sock);
+	int ret;
+
+	if (mnl_socket_sendto(nf_sock, data, len) < 0)
+		return -1;
+
+	ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+	while (ret > 0) {
+		ret = mnl_cb_run(buf, ret, seq, portid, cb, cb_data);
+		if (ret <= 0)
+			goto out;
+
+		ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+	}
+out:
+	if (ret < 0 && errno == EAGAIN)
+		return 0;
+
+	return ret;
+}
+
+/*
+ * Rule
+ */
+int mnl_nft_rule_add(struct mnl_socket *nf_sock, struct nft_rule *nlr,
+		     unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE,
+			nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY),
+			NLM_F_APPEND|NLM_F_ACK|NLM_F_CREATE, seq);
+	nft_rule_nlmsg_build_payload(nlh, nlr);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_rule_delete(struct mnl_socket *nf_sock, struct nft_rule *nlr,
+			unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELRULE,
+			nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY),
+			NLM_F_ACK, seq);
+	nft_rule_nlmsg_build_payload(nlh, nlr);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int rule_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_rule_list *nlr_list = data;
+	struct nft_rule *r;
+
+	r = nft_rule_alloc();
+	if (r == NULL)
+		memory_allocation_error();
+
+	if (nft_rule_nlmsg_parse(nlh, r) < 0)
+		goto err_free;
+
+	nft_rule_list_add(r, nlr_list);
+	return MNL_CB_OK;
+
+err_free:
+	nft_rule_free(r);
+	return MNL_CB_OK;
+}
+
+struct nft_rule_list *mnl_nft_rule_dump(struct mnl_socket *nf_sock, int family)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_rule_list *nlr_list;
+	int ret;
+
+	nlr_list = nft_rule_list_alloc();
+	if (nlr_list == NULL)
+		memory_allocation_error();
+
+	nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
+				       NLM_F_DUMP, seq);
+
+	ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, rule_cb, nlr_list);
+	if (ret < 0)
+		goto err;
+
+	return nlr_list;
+err:
+	nft_rule_list_free(nlr_list);
+	return NULL;
+}
+
+/*
+ * Chain
+ */
+int mnl_nft_chain_add(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+		      unsigned int flags)
+
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+			nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+			NLM_F_ACK|flags, seq);
+	nft_chain_nlmsg_build_payload(nlh, nlc);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_chain_delete(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+			 unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN,
+			nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+			NLM_F_ACK, seq);
+	nft_chain_nlmsg_build_payload(nlh, nlc);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_chain_list *nlc_list = data;
+	struct nft_chain *c;
+
+	c = nft_chain_alloc();
+	if (c == NULL)
+		memory_allocation_error();
+
+	if (nft_chain_nlmsg_parse(nlh, c) < 0)
+		goto err_free;
+
+	nft_chain_list_add(c, nlc_list);
+	return MNL_CB_OK;
+
+err_free:
+	nft_chain_free(c);
+	return MNL_CB_OK;
+}
+
+struct nft_chain_list *mnl_nft_chain_dump(struct mnl_socket *nf_sock, int family)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_chain_list *nlc_list;
+	int ret;
+
+	nlc_list = nft_chain_list_alloc();
+	if (nlc_list == NULL)
+		memory_allocation_error();
+
+	nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family,
+					NLM_F_DUMP, seq);
+
+	ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, chain_cb, nlc_list);
+	if (ret < 0)
+		goto err;
+
+	return nlc_list;
+err:
+	nft_chain_list_free(nlc_list);
+	return NULL;
+}
+
+static int chain_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+	nft_chain_nlmsg_parse(nlh, data);
+	return MNL_CB_OK;
+}
+
+int mnl_nft_chain_get(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+		      unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
+			nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+			NLM_F_ACK|flags, seq);
+	nft_chain_nlmsg_build_payload(nlh, nlc);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, chain_get_cb, nlc);
+}
+
+/*
+ * Table
+ */
+int mnl_nft_table_add(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE,
+			nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+			NLM_F_EXCL|NLM_F_ACK, seq);
+	nft_table_nlmsg_build_payload(nlh, nlt);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_table_delete(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELTABLE,
+			nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+			NLM_F_ACK, seq);
+	nft_table_nlmsg_build_payload(nlh, nlt);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int table_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_table_list *nlt_list = data;
+	struct nft_table *t;
+
+	t = nft_table_alloc();
+	if (t == NULL)
+		memory_allocation_error();
+
+	if (nft_table_nlmsg_parse(nlh, t) < 0)
+		goto err_free;
+
+	nft_table_list_add(t, nlt_list);
+	return MNL_CB_OK;
+
+err_free:
+	nft_table_free(t);
+	return MNL_CB_OK;
+}
+
+struct nft_table_list *mnl_nft_table_dump(struct mnl_socket *nf_sock, int family)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_table_list *nlt_list;
+	int ret;
+
+	nlt_list = nft_table_list_alloc();
+	if (nlt_list == NULL)
+		memory_allocation_error();
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family,
+					NLM_F_DUMP, seq);
+
+	ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, table_cb, nlt_list);
+	if (ret < 0)
+		goto err;
+
+	return nlt_list;
+err:
+	nft_table_list_free(nlt_list);
+	return NULL;
+}
+
+static int table_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_table *t = data;
+
+	nft_table_nlmsg_parse(nlh, t);
+	return MNL_CB_OK;
+}
+
+int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
+		      unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE,
+					nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+					NLM_F_ACK, seq);
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, table_get_cb, nlt);
+}
+
+/*
+ * Set
+ */
+static int set_add_cb(const struct nlmsghdr *nlh, void *data)
+{
+	nft_set_nlmsg_parse(nlh, data);
+	return MNL_CB_OK;
+}
+
+int mnl_nft_set_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+		    unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_NEWSET,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      flags|NLM_F_CREATE|NLM_F_ACK, seq);
+	nft_set_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_add_cb, nls);
+}
+
+int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+		       unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_DELSET,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      flags|NLM_F_ACK, seq);
+	nft_set_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int set_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_set_list *nls_list = data;
+	struct nft_set *s;
+
+	s = nft_set_alloc();
+	if (s == NULL)
+		memory_allocation_error();
+
+	if (nft_set_nlmsg_parse(nlh, s) < 0)
+		goto err_free;
+
+	nft_set_list_add(s, nls_list);
+	return MNL_CB_OK;
+
+err_free:
+	nft_set_free(s);
+	return MNL_CB_OK;
+}
+
+struct nft_set_list *
+mnl_nft_set_dump(struct mnl_socket *nf_sock, int family, const char *table)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	struct nft_set *s;
+	struct nft_set_list *nls_list;
+	int ret;
+
+	s = nft_set_alloc();
+	if (s == NULL)
+		memory_allocation_error();
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
+				      NLM_F_DUMP|NLM_F_ACK, seq);
+	nft_set_attr_set(s, NFT_SET_ATTR_TABLE, table);
+	nft_set_nlmsg_build_payload(nlh, s);
+	nft_set_free(s);
+
+	nls_list = nft_set_list_alloc();
+	if (nls_list == NULL)
+		memory_allocation_error();
+
+	ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_cb, nls_list);
+	if (ret < 0)
+		goto err;
+
+	return nls_list;
+err:
+	nft_set_list_free(nls_list);
+	return NULL;
+}
+
+static int set_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nft_set *s = data;
+
+	nft_set_nlmsg_parse(nlh, s);
+	return MNL_CB_OK;
+}
+
+int mnl_nft_set_get(struct mnl_socket *nf_sock, struct nft_set *nls)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      NLM_F_ACK, seq);
+	nft_set_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_get_cb, nls);
+}
+
+/*
+ * Set elements
+ */
+int mnl_nft_setelem_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+			unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_NEWSETELEM,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK, seq);
+	nft_set_elems_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_setelem_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+			   unsigned int flags)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_DELSETELEM,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      NLM_F_ACK, seq);
+	nft_set_elems_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
+{
+	nft_set_elems_nlmsg_parse(nlh, data);
+	return MNL_CB_OK;
+}
+
+int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+
+	nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+				      nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+				      NLM_F_DUMP|NLM_F_ACK, seq);
+	nft_set_nlmsg_build_payload(nlh, nls);
+
+	return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_elem_cb, nls);
+}
diff --git a/src/netlink.c b/src/netlink.c
index da290d5..d835281 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2008-2012 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -8,59 +9,39 @@
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
 
-#include <netlink/netfilter/nfnl.h>
-#include <netlink/netfilter/netfilter.h>
-#include <netlink/netfilter/nft_table.h>
-#include <netlink/netfilter/nft_chain.h>
-#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 <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/expr.h>
+#include <libnftables/set.h>
 #include <linux/netfilter/nf_tables.h>
 
 #include <nftables.h>
 #include <netlink.h>
+#include <mnl.h>
 #include <expression.h>
 #include <gmputil.h>
 #include <utils.h>
 #include <erec.h>
 
-static struct nl_sock *nf_sock;
+static struct mnl_socket *nf_sock;
 
 static void __init netlink_open_sock(void)
 {
-	nlmsg_set_default_size(65536);
-	nf_sock = nl_socket_alloc();
+	nf_sock = mnl_socket_open(NETLINK_NETFILTER);
 	if (nf_sock == NULL)
 		memory_allocation_error();
 
-	nfnl_connect(nf_sock);
-	nl_socket_set_nonblocking(nf_sock);
+	fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK);
 }
 
 static void __exit netlink_close_sock(void)
 {
-	nl_socket_free(nf_sock);
-}
-
-static void netlink_set_callback(nl_recvmsg_msg_cb_t func, void *arg)
-{
-	nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM, func, arg);
-}
-
-void netlink_dump_object(struct nl_object *obj)
-{
-#ifdef DEBUG
-	struct nl_dump_params params = {
-		.dp_fd		= stdout,
-		.dp_type	= NL_DUMP_DETAILS,
-	};
-
-	if (!(debug_level & DEBUG_NETLINK))
-		return;
-	nl_object_dump(obj, &params);
-#endif
+	mnl_socket_close(nf_sock);
 }
 
 static int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
@@ -79,119 +60,128 @@ static int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
 	return -1;
 }
 
-struct nfnl_nft_table *alloc_nft_table(const struct handle *h)
+struct nft_table *alloc_nft_table(const struct handle *h)
 {
-	struct nfnl_nft_table *nlt;
+	struct nft_table *nlt;
 
-	nlt = nfnl_nft_table_alloc();
+	nlt = nft_table_alloc();
 	if (nlt == NULL)
 		memory_allocation_error();
-	nfnl_nft_table_set_family(nlt, h->family);
+
+	nft_table_attr_set_u32(nlt, NFT_TABLE_ATTR_FAMILY, h->family);
 	if (h->table != NULL)
-		nfnl_nft_table_set_name(nlt, h->table, strlen(h->table) + 1);
+		nft_table_attr_set(nlt, NFT_TABLE_ATTR_NAME, h->table);
 	return nlt;
 }
 
-struct nfnl_nft_chain *alloc_nft_chain(const struct handle *h)
+struct nft_chain *alloc_nft_chain(const struct handle *h)
 {
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain *nlc;
 
-	nlc = nfnl_nft_chain_alloc();
+	nlc = nft_chain_alloc();
 	if (nlc == NULL)
 		memory_allocation_error();
-	nfnl_nft_chain_set_family(nlc, h->family);
-	nfnl_nft_chain_set_table(nlc, h->table, strlen(h->table) + 1);
+
+	nft_chain_attr_set_u32(nlc, NFT_CHAIN_ATTR_FAMILY, h->family);
+	nft_chain_attr_set_str(nlc, NFT_CHAIN_ATTR_TABLE, h->table);
 	if (h->handle != 0)
-		nfnl_nft_chain_set_handle(nlc, h->handle);
+		nft_chain_attr_set_u64(nlc, NFT_CHAIN_ATTR_HANDLE, h->handle);
 	if (h->chain != NULL)
-		nfnl_nft_chain_set_name(nlc, h->chain, strlen(h->chain) + 1);
+		nft_chain_attr_set_str(nlc, NFT_CHAIN_ATTR_NAME, h->chain);
 	return nlc;
 }
 
-struct nfnl_nft_rule *alloc_nft_rule(const struct handle *h)
+struct nft_rule *alloc_nft_rule(const struct handle *h)
 {
-	struct nfnl_nft_rule *nlr;
+	struct nft_rule *nlr;
 
-	nlr = nfnl_nft_rule_alloc();
+	nlr = nft_rule_alloc();
 	if (nlr == NULL)
 		memory_allocation_error();
-	nfnl_nft_rule_set_family(nlr, h->family);
-	nfnl_nft_rule_set_table(nlr, h->table, strlen(h->table) + 1);
+
+	nft_rule_attr_set_u32(nlr, NFT_RULE_ATTR_FAMILY, h->family);
+	nft_rule_attr_set_str(nlr, NFT_RULE_ATTR_TABLE, h->table);
 	if (h->chain != NULL)
-		nfnl_nft_rule_set_chain(nlr, h->chain, strlen(h->chain) + 1);
+		nft_rule_attr_set_str(nlr, NFT_RULE_ATTR_CHAIN, h->chain);
 	if (h->handle)
-		nfnl_nft_rule_set_handle(nlr, h->handle);
+		nft_rule_attr_set_u64(nlr, NFT_RULE_ATTR_HANDLE, h->handle);
 	return nlr;
 }
 
-struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *))
+struct nft_rule_expr *alloc_nft_expr(const char *name)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
-	nle = nfnl_nft_expr_alloc();
-	if (nle == NULL || init(nle) != 0)
+	nle = nft_rule_expr_alloc(name);
+	if (nle == NULL)
 		memory_allocation_error();
 	return nle;
 }
 
-struct nfnl_nft_set *alloc_nft_set(const struct handle *h)
+struct nft_set *alloc_nft_set(const struct handle *h)
 {
-	struct nfnl_nft_set *nls;
+	struct nft_set *nls;
 
-	nls = nfnl_nft_set_alloc();
+	nls = 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);
+
+	nft_set_attr_set_u32(nls, NFT_SET_ATTR_FAMILY, h->family);
+	nft_set_attr_set_str(nls, NFT_SET_ATTR_TABLE, h->table);
 	if (h->set != NULL)
-		nfnl_nft_set_set_name(nls, h->set, strlen(h->set) + 1);
+		nft_set_attr_set_str(nls, NFT_SET_ATTR_NAME, h->set);
+
 	return nls;
 }
 
-static struct nfnl_nft_setelem *alloc_nft_setelem(const struct expr *expr)
+static struct nft_set_elem *alloc_nft_setelem(const struct expr *expr)
 {
-	struct nfnl_nft_setelem *nlse;
+	struct nft_set_elem *nlse;
+	struct nft_data_linearize nld;
 
-	nlse = nfnl_nft_setelem_alloc();
+	nlse = nft_set_elem_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 {
+	if (expr->ops->type == EXPR_VALUE ||
+	    expr->flags & EXPR_F_INTERVAL_END) {
+		netlink_gen_data(expr, &nld);
+		nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY,
+				      &nld.value, nld.len);
+	} 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));
+		netlink_gen_data(expr->left, &nld);
+		nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY,
+				      &nld.value, nld.len);
+		netlink_gen_data(expr->right, &nld);
+		if (expr->right->ops->type == EXPR_VERDICT) {
+			nft_set_elem_attr_set_u32(nlse, NFT_SET_ELEM_ATTR_VERDICT,
+						  expr->right->verdict);
+			if (expr->chain != NULL) {
+				nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_CHAIN,
+						nld.chain, strlen(nld.chain));
+			}
+		}
 	}
 
-	if (expr->flags & EXPR_F_INTERVAL_END)
-		nfnl_nft_setelem_set_flags(nlse, NFT_SET_ELEM_INTERVAL_END);
+	if (expr->flags & EXPR_F_INTERVAL_END) {
+		nft_set_elem_attr_set_u32(nlse, NFT_SET_ELEM_ATTR_FLAGS,
+					  NFT_SET_ELEM_INTERVAL_END);
+	}
 
 	return nlse;
 }
 
-struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len)
+void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder,
+			  unsigned int len, struct nft_data_linearize *data)
 {
-	struct nfnl_nft_data *nld;
-
 	assert(len > 0);
-	nld = nfnl_nft_data_alloc(data, len);
-	if (nld == NULL)
-		memory_allocation_error();
-	return nld;
+	mpz_export_data(data->value, value, byteorder, len);
+	data->len = len;
 }
 
-struct nfnl_nft_data *netlink_gen_raw_data(const mpz_t value,
-					   enum byteorder byteorder,
-					   unsigned int len)
-{
-	unsigned char data[len];
-
-	mpz_export_data(data, value, byteorder, len);
-	return alloc_nft_data(data, len);
-}
-
-static struct nfnl_nft_data *netlink_gen_concat_data(const struct expr *expr)
+static void netlink_gen_concat_data(const struct expr *expr,
+				    struct nft_data_linearize *nld)
 {
 	const struct expr *i;
 	unsigned int len, offset;
@@ -210,79 +200,75 @@ static struct nfnl_nft_data *netlink_gen_concat_data(const struct expr *expr)
 					i->len / BITS_PER_BYTE);
 			offset += i->len / BITS_PER_BYTE;
 		}
-	
-		return alloc_nft_data(data, len / BITS_PER_BYTE);
+
+		memcpy(nld->value, data, len / BITS_PER_BYTE);
+		nld->len = len;
 	}
 }
 
-static struct nfnl_nft_data *netlink_gen_constant_data(const struct expr *expr)
+static void netlink_gen_constant_data(const struct expr *expr,
+				      struct nft_data_linearize *data)
 {
 	assert(expr->ops->type == EXPR_VALUE);
-	return netlink_gen_raw_data(expr->value, expr->byteorder,
-				    div_round_up(expr->len, BITS_PER_BYTE));
+	netlink_gen_raw_data(expr->value, expr->byteorder,
+			     div_round_up(expr->len, BITS_PER_BYTE), data);
 }
 
-static struct nfnl_nft_data *netlink_gen_verdict(const struct expr *expr)
+static void netlink_gen_verdict(const struct expr *expr,
+				struct nft_data_linearize *data)
 {
-	struct nfnl_nft_data *verdict;
-
-	verdict = nfnl_nft_verdict_alloc();
-	nfnl_nft_verdict_set_verdict(verdict, expr->verdict);
+	data->verdict = expr->verdict;
 
 	switch (expr->verdict) {
 	case NFT_JUMP:
 	case NFT_GOTO:
-		nfnl_nft_verdict_set_chain(verdict, expr->chain);
+		strncpy(data->chain, expr->chain, NFT_CHAIN_MAXNAMELEN);
+		data->chain[NFT_CHAIN_MAXNAMELEN-1] = '\0';
 		break;
 	}
-
-	return verdict;
 }
 
-struct nfnl_nft_data *netlink_gen_data(const struct expr *expr)
+void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
 {
 	switch (expr->ops->type) {
 	case EXPR_VALUE:
-		return netlink_gen_constant_data(expr);
+		return netlink_gen_constant_data(expr, data);
 	case EXPR_CONCAT:
-		return netlink_gen_concat_data(expr);
+		return netlink_gen_concat_data(expr, data);
 	case EXPR_VERDICT:
-		return netlink_gen_verdict(expr);
+		return netlink_gen_verdict(expr, data);
 	default:
 		BUG("invalid data expression type %s\n", expr->ops->name);
 	}
 }
 
 struct expr *netlink_alloc_value(const struct location *loc,
-				 const struct nfnl_nft_data *nld)
+				 const struct nft_data_delinearize *nld)
 {
 	return constant_expr_alloc(loc, &invalid_type, BYTEORDER_INVALID,
-				   nfnl_nft_data_get_size(nld) * BITS_PER_BYTE,
-				   nfnl_nft_data_get(nld));
+				   nld->len * BITS_PER_BYTE, nld->value);
 }
 
 static struct expr *netlink_alloc_verdict(const struct location *loc,
-					  const struct nfnl_nft_data *nld)
+					  const struct nft_data_delinearize *nld)
 {
-	unsigned int code;
 	char *chain;
 
-	code = nfnl_nft_verdict_get_verdict(nld);
-	switch (code) {
+	switch (nld->verdict) {
 	case NFT_JUMP:
 	case NFT_GOTO:
-		chain = xstrdup(nfnl_nft_verdict_get_chain(nld));
+		chain = xstrdup(nld->chain);
 		break;
 	default:
 		chain = NULL;
 		break;
 	}
 
-	return verdict_expr_alloc(loc, code, chain);
+	return verdict_expr_alloc(loc, nld->verdict, chain);
 }
 
 struct expr *netlink_alloc_data(const struct location *loc,
-				const struct nfnl_nft_data *nld,
+				const struct nft_data_delinearize *nld,
 				enum nft_registers dreg)
 {
 	switch (dreg) {
@@ -296,230 +282,246 @@ struct expr *netlink_alloc_data(const struct location *loc,
 int netlink_add_rule(struct netlink_ctx *ctx, const struct handle *h,
 		     const struct rule *rule, uint32_t flags)
 {
-	struct nfnl_nft_rule *nlr;
+	struct nft_rule *nlr;
 	int err;
 
 	nlr = alloc_nft_rule(&rule->handle);
 	err = netlink_linearize_rule(ctx, nlr, rule);
 	if (err == 0) {
-		err = nfnl_nft_rule_add(nf_sock, nlr, flags | NLM_F_EXCL);
+		err = mnl_nft_rule_add(nf_sock, nlr, flags | NLM_F_EXCL);
 		if (err < 0)
 			netlink_io_error(ctx, &rule->location,
-					 "Could not add rule: %s", nl_geterror(err));
+					 "Could not add rule: %s",
+					 strerror(errno));
 	}
-	nfnl_nft_rule_put(nlr);
+	nft_rule_free(nlr);
 	return err;
 }
 
 int netlink_delete_rule(struct netlink_ctx *ctx, const struct handle *h,
 			const struct location *loc)
 {
-	struct nfnl_nft_rule *nlr;
+	struct nft_rule *nlr;
 	int err;
 
 	nlr = alloc_nft_rule(h);
-	err = nfnl_nft_rule_delete(nf_sock, nlr, 0);
-	nfnl_nft_rule_put(nlr);
+	err = mnl_nft_rule_delete(nf_sock, nlr, 0);
+	nft_rule_free(nlr);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not delete rule: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
-static void list_rule_cb(struct nl_object *obj, void *arg)
+void netlink_dump_rule(struct nft_rule *nlr)
 {
-	const struct nfnl_nft_rule *nlr = (struct nfnl_nft_rule *)obj;
-	struct netlink_ctx *ctx = arg;
-	struct rule *rule;
+#ifdef DEBUG
+	char buf[4096];
 
-	netlink_dump_object(obj);
-	if (!nfnl_nft_rule_test_family(nlr) ||
-	    !nfnl_nft_rule_test_table(nlr) ||
-	    !nfnl_nft_rule_test_chain(nlr) ||
-	    !nfnl_nft_rule_test_handle(nlr)) {
-		netlink_io_error(ctx, NULL, "Incomplete rule received");
+	if (!(debug_level & DEBUG_NETLINK))
 		return;
-	}
 
-	rule = netlink_delinearize_rule(ctx, obj);
-	list_add_tail(&rule->list, &ctx->list);
+	nft_rule_snprintf(buf, sizeof(buf), nlr, 0, 0);
+	fprintf(stderr, "%s\n", buf);
+#endif
 }
 
-static int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h,
-			      const struct location *loc)
+void netlink_dump_expr(struct nft_rule_expr *nle)
 {
-	struct nl_cache *rule_cache;
-	struct nfnl_nft_rule *nlr;
-	int err;
+#ifdef DEBUG
+	char buf[4096];
 
-	err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache);
-	if (err < 0)
-		return netlink_io_error(ctx, loc,
-					"Could not receive rules from kernel: %s",
-					nl_geterror(err));
+	if (!(debug_level & DEBUG_NETLINK))
+		return;
 
-	nlr = alloc_nft_rule(h);
-	nl_cache_foreach_filter(rule_cache, OBJ_CAST(nlr), list_rule_cb, ctx);
-	nfnl_nft_rule_put(nlr);
-	nl_cache_free(rule_cache);
-	return 0;
+	nft_rule_expr_snprintf(buf, sizeof(buf), nle, 0, 0);
+	fprintf(stderr, "%s\n", buf);
+#endif
 }
 
-static int netlink_get_rule_cb(struct nl_msg *msg, void *arg)
+static int list_rule_cb(struct nft_rule *nlr, void *arg)
 {
-	return nl_msg_parse(msg, list_rule_cb, arg);
+	struct netlink_ctx *ctx = arg;
+	const struct handle *h = ctx->data;
+	struct rule *rule;
+
+	if ((h->family != nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY)) ||
+	    strcmp(nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE), h->table) != 0 ||
+	    (h->chain &&
+	     strcmp(nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_CHAIN), h->chain) != 0))
+		return 0;
+
+	netlink_dump_rule(nlr);
+	rule = netlink_delinearize_rule(ctx, nlr);
+	list_add_tail(&rule->list, &ctx->list);
+
+	return 0;
 }
 
-int netlink_get_rule(struct netlink_ctx *ctx, const struct handle *h,
-		     const struct location *loc)
+static int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h,
+			      const struct location *loc)
 {
-	struct nfnl_nft_rule *nlr;
-	int err;
-
-	nlr = alloc_nft_rule(h);
-	netlink_set_callback(netlink_get_rule_cb, ctx);
-	err = nfnl_nft_rule_query(nf_sock, nlr, 0);
-	if (err == 0)
-		err = nl_recvmsgs_default(nf_sock);
-	nfnl_nft_rule_put(nlr);
+	struct nft_rule_list *rule_cache;
 
-	if (err < 0)
+	rule_cache = mnl_nft_rule_dump(nf_sock, h->family);
+	if (rule_cache == NULL)
 		return netlink_io_error(ctx, loc,
-					"Could not receive rule from kernel: %s",
-					nl_geterror(err));
-	return err;
+					"Could not receive rules from kernel: %s",
+					strerror(errno));
+
+	ctx->data = h;
+	nft_rule_list_foreach(rule_cache, list_rule_cb, ctx);
+	nft_rule_list_free(rule_cache);
+	return 0;
 }
 
-static void flush_rule_cb(struct nl_object *obj, void *arg)
+static int flush_rule_cb(struct nft_rule *nlr, void *arg)
 {
 	struct netlink_ctx *ctx = arg;
 	int err;
 
-	netlink_dump_object(obj);
-	err = nfnl_nft_rule_delete(nf_sock, (struct nfnl_nft_rule *)obj, 0);
-	if (err < 0)
+	netlink_dump_rule(nlr);
+	err = mnl_nft_rule_delete(nf_sock, nlr, 0);
+	if (err < 0) {
 		netlink_io_error(ctx, NULL, "Could not delete rule: %s",
-				 nl_geterror(err));
+				 strerror(errno));
+		return err;
+	}
+	return 0;
 }
 
 static int netlink_flush_rules(struct netlink_ctx *ctx, const struct handle *h,
 			       const struct location *loc)
 {
-	struct nl_cache *rule_cache;
-	struct nfnl_nft_rule *nlr;
-	int err;
+	struct nft_rule_list *rule_cache;
+	struct nft_rule *nlr;
 
-	err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache);
-	if (err < 0)
+	rule_cache = mnl_nft_rule_dump(nf_sock, h->family);
+	if (rule_cache == NULL)
 		return netlink_io_error(ctx, loc,
 					"Could not receive rules from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 
 	nlr = alloc_nft_rule(h);
-	nl_cache_foreach_filter(rule_cache, OBJ_CAST(nlr), flush_rule_cb, ctx);
-	nfnl_nft_rule_put(nlr);
-	nl_cache_free(rule_cache);
+	nft_rule_list_foreach(rule_cache, flush_rule_cb, ctx);
+	nft_rule_free(nlr);
+	nft_rule_list_free(rule_cache);
 	return 0;
 }
 
+void netlink_dump_chain(struct nft_chain *nlc)
+{
+#ifdef DEBUG
+	char buf[4096];
+
+	if (!(debug_level & DEBUG_NETLINK))
+		return;
+
+	nft_chain_snprintf(buf, sizeof(buf), nlc, 0, 0);
+	fprintf(stderr, "%s\n", buf);
+#endif
+}
+
 int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc, const struct chain *chain)
 {
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain *nlc;
 	int err;
 
 	nlc = alloc_nft_chain(h);
 	if (chain != NULL && chain->flags & CHAIN_F_BASECHAIN) {
-		nfnl_nft_chain_set_hooknum(nlc, chain->hooknum);
-		nfnl_nft_chain_set_priority(nlc, chain->priority);
+		nft_chain_attr_set_u32(nlc, NFT_CHAIN_ATTR_HOOKNUM,
+				       chain->hooknum);
+		nft_chain_attr_set_u32(nlc, NFT_CHAIN_ATTR_PRIO,
+				       chain->priority);
 	}
-	netlink_dump_object(OBJ_CAST(nlc));
-	err = nfnl_nft_chain_add(nf_sock, nlc, NLM_F_EXCL);
-	nfnl_nft_chain_put(nlc);
+	netlink_dump_chain(nlc);
+	err = mnl_nft_chain_add(nf_sock, nlc, NLM_F_EXCL);
+	nft_chain_free(nlc);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not add chain: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
 int netlink_rename_chain(struct netlink_ctx *ctx, const struct handle *h,
 			 const struct location *loc, const char *name)
 {
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain *nlc;
 	int err;
 
 	nlc = alloc_nft_chain(h);
-	nfnl_nft_chain_set_name(nlc, name, strlen(name) + 1);
-	netlink_dump_object(OBJ_CAST(nlc));
-	err = nfnl_nft_chain_add(nf_sock, nlc, 0);
-	nfnl_nft_chain_put(nlc);
+	nft_chain_attr_set_str(nlc, NFT_CHAIN_ATTR_NAME, name);
+	netlink_dump_chain(nlc);
+	err = mnl_nft_chain_add(nf_sock, nlc, 0);
+	nft_chain_free(nlc);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not rename chain: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
 int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h,
 			 const struct location *loc)
 {
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain *nlc;
 	int err;
 
 	nlc = alloc_nft_chain(h);
-	netlink_dump_object(OBJ_CAST(nlc));
-	err = nfnl_nft_chain_delete(nf_sock, nlc, 0);
-	nfnl_nft_chain_put(nlc);
+	netlink_dump_chain(nlc);
+	err = mnl_nft_chain_delete(nf_sock, nlc, 0);
+	nft_chain_free(nlc);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not delete chain: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
-static void list_chain_cb(struct nl_object *obj, void *arg)
+static int list_chain_cb(struct nft_chain *nlc, void *arg)
 {
-	struct nfnl_nft_chain *nlc = (struct nfnl_nft_chain *)obj;
 	struct netlink_ctx *ctx = arg;
+	const struct handle *h = ctx->data;
 	struct chain *chain;
 
-	netlink_dump_object(obj);
-	if (!nfnl_nft_chain_test_family(nlc) ||
-	    !nfnl_nft_chain_test_table(nlc) ||
-	    !nfnl_nft_chain_test_name(nlc)) {
-		netlink_io_error(ctx, NULL, "Incomplete chain received");
-		return;
-	}
+	if ((h->family != nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY)) ||
+	    strcmp(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE), h->table) != 0)
+		return 0;
 
-	chain = chain_alloc(nfnl_nft_chain_get_name(nlc));
-	chain->handle.family = nfnl_nft_chain_get_family(nlc);
-	chain->handle.table  = xstrdup(nfnl_nft_chain_get_table(nlc));
-	chain->handle.handle = nfnl_nft_chain_get_handle(nlc);
-	chain->hooknum       = nfnl_nft_chain_get_hooknum(nlc);
-	chain->priority      = nfnl_nft_chain_get_priority(nlc);
+	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
+	chain->handle.family =
+		nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
+	chain->handle.table  =
+		xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
+	chain->handle.handle =
+		nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
+	chain->hooknum       =
+		nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_HOOKNUM);
+	chain->priority      =
+		nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_PRIO);
 	list_add_tail(&chain->list, &ctx->list);
+
+	return 0;
 }
 
 int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h,
 			const struct location *loc)
 {
-	struct nl_cache *chain_cache;
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain_list *chain_cache;
 	struct chain *chain;
-	int err;
 
-	err = nfnl_nft_chain_alloc_cache(nf_sock, &chain_cache);
-	if (err < 0)
+	chain_cache = mnl_nft_chain_dump(nf_sock, h->family);
+	if (chain_cache == NULL)
 		return netlink_io_error(ctx, loc,
 					"Could not receive chains from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 
-	nlc = alloc_nft_chain(h);
-	nl_cache_foreach_filter(chain_cache, OBJ_CAST(nlc), list_chain_cb, ctx);
-	nfnl_nft_chain_put(nlc);
-	nl_cache_free(chain_cache);
+	ctx->data = h;
+	nft_chain_list_foreach(chain_cache, list_chain_cb, ctx);
+	nft_chain_list_free(chain_cache);
 
 	/* Caller wants all existing chains */
 	if (h->chain == NULL)
@@ -534,32 +536,33 @@ int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h,
 	return netlink_io_error(ctx, NULL,
 				"Could not find chain `%s' in table `%s': %s",
 				h->chain, h->table,
-				nl_geterror(NLE_OBJ_NOTFOUND));
-}
-
-static int netlink_get_chain_cb(struct nl_msg *msg, void *arg)
-{
-	return nl_msg_parse(msg, list_chain_cb, arg);
+				strerror(ENOENT));
 }
 
 int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc)
 {
-	struct nfnl_nft_chain *nlc;
+	struct nft_chain *nlc;
+	struct chain *chain;
 	int err;
 
 	nlc = alloc_nft_chain(h);
-	netlink_set_callback(netlink_get_chain_cb, ctx);
-	err = nfnl_nft_chain_query(nf_sock, nlc, 0);
-	if (err == 0)
-		err = nl_recvmsgs_default(nf_sock);
-	netlink_set_callback(NULL, NULL);
-	nfnl_nft_chain_put(nlc);
+	err = mnl_nft_chain_get(nf_sock, nlc, 0);
+
+	chain = chain_alloc(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_NAME));
+	chain->handle.family = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY);
+	chain->handle.table  = xstrdup(nft_chain_attr_get_str(nlc, NFT_CHAIN_ATTR_TABLE));
+	chain->handle.handle = nft_chain_attr_get_u64(nlc, NFT_CHAIN_ATTR_HANDLE);
+	chain->hooknum       = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_HOOKNUM);
+	chain->priority      = nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_PRIO);
+	list_add_tail(&chain->list, &ctx->list);
+
+	nft_chain_free(nlc);
 
 	if (err < 0)
 		return netlink_io_error(ctx, loc,
 					"Could not receive chain from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 	return err;
 }
 
@@ -578,96 +581,97 @@ int netlink_flush_chain(struct netlink_ctx *ctx, const struct handle *h,
 int netlink_add_table(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc, const struct table *table)
 {
-	struct nfnl_nft_table *nlt;
+	struct nft_table *nlt;
 	int err;
 
 	nlt = alloc_nft_table(h);
-	err = nfnl_nft_table_add(nf_sock, nlt, NLM_F_EXCL);
-	nfnl_nft_table_put(nlt);
+	err = mnl_nft_table_add(nf_sock, nlt, NLM_F_EXCL);
+	nft_table_free(nlt);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not add table: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
 int netlink_delete_table(struct netlink_ctx *ctx, const struct handle *h,
 			 const struct location *loc)
 {
-	struct nfnl_nft_table *nlt;
+	struct nft_table *nlt;
 	int err;
 
 	nlt = alloc_nft_table(h);
-	err = nfnl_nft_table_delete(nf_sock, nlt, 0);
-	nfnl_nft_table_put(nlt);
+	err = mnl_nft_table_delete(nf_sock, nlt, 0);
+	nft_table_free(nlt);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not delete table: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
-static void list_table_cb(struct nl_object *obj, void *arg)
+void netlink_dump_table(struct nft_table *nlt)
 {
-	struct nfnl_nft_table *nlt = (struct nfnl_nft_table *)obj;
-	struct netlink_ctx *ctx = arg;
-	struct table *table;
+#ifdef DEBUG
+	char buf[4096];
 
-	netlink_dump_object(obj);
-	if (!nfnl_nft_table_test_family(nlt) ||
-	    !nfnl_nft_table_test_name(nlt)) {
-		netlink_io_error(ctx, NULL, "Incomplete table received");
+	if (!(debug_level & DEBUG_NETLINK))
 		return;
-	}
 
+	nft_table_snprintf(buf, sizeof(buf), nlt, 0, 0);
+	fprintf(stderr, "%s\n", buf);
+#endif
+}
+
+static int list_table_cb(struct nft_table *nlt, void *arg)
+{
+	struct netlink_ctx *ctx = arg;
+	struct table *table;
+
+	netlink_dump_table(nlt);
 	table = table_alloc();
-	table->handle.family = nfnl_nft_table_get_family(nlt);
-	table->handle.table  = xstrdup(nfnl_nft_table_get_name(nlt));
+	table->handle.family =
+		nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY);
+	table->handle.table  =
+		xstrdup(nft_table_attr_get_str(nlt, NFT_TABLE_ATTR_NAME));
 	list_add_tail(&table->list, &ctx->list);
+
+	return 0;
 }
 
 int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
 			const struct location *loc)
 {
-	struct nl_cache *table_cache;
-	struct nfnl_nft_table *nlt;
-	int err;
+	struct nft_table_list *table_cache;
+	struct nft_table *nlt;
 
-	err = nfnl_nft_table_alloc_cache(nf_sock, &table_cache);
-	if (err < 0)
+	table_cache = mnl_nft_table_dump(nf_sock, h->family);
+	if (table_cache == NULL)
 		return netlink_io_error(ctx, loc,
 					"Could not receive tables from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 
 	nlt = alloc_nft_table(h);
-	nl_cache_foreach_filter(table_cache, OBJ_CAST(nlt), list_table_cb, ctx);
-	nfnl_nft_table_put(nlt);
-	nl_cache_free(table_cache);
+	nft_table_list_foreach(table_cache, list_table_cb, ctx);
+	nft_table_free(nlt);
+	nft_table_list_free(table_cache);
 	return 0;
 }
 
-static int netlink_get_table_cb(struct nl_msg *msg, void *arg)
-{
-	return nl_msg_parse(msg, list_table_cb, arg);
-}
-
 int netlink_get_table(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc)
 {
-	struct nfnl_nft_table *nlt;
+	struct nft_table *nlt;
 	int err;
 
 	nlt = alloc_nft_table(h);
-	netlink_set_callback(netlink_get_table_cb, ctx);
-	err = nfnl_nft_table_query(nf_sock, nlt, 0);
-	if (err == 0)
-		err = nl_recvmsgs_default(nf_sock);
-	nfnl_nft_table_put(nlt);
+	err = mnl_nft_table_get(nf_sock, nlt, 0);
+	nft_table_free(nlt);
 
 	if (err < 0)
 		return netlink_io_error(ctx, loc,
 					"Could not receive table from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 	return err;
 }
 
@@ -704,268 +708,261 @@ static const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
 	}
 }
 
-static void add_set_cb(struct nl_object *obj, void *arg)
+void netlink_dump_set(struct nft_set *nls)
 {
-	struct nfnl_nft_set *nls = (struct nfnl_nft_set *)obj;
-	struct netlink_ctx *ctx = arg;
-	struct set *set = ctx->set;
+#ifdef DEBUG
+	char buf[4096];
 
-	netlink_dump_object(OBJ_CAST(nls));
-	set->handle.set = xstrdup(nfnl_nft_set_get_name(nls));
-}
+	if (!(debug_level & DEBUG_NETLINK))
+		return;
 
-static int netlink_add_set_cb(struct nl_msg *msg, void *arg)
-{
-	return nl_msg_parse(msg, add_set_cb, arg);
+	nft_set_snprintf(buf, sizeof(buf), nls, 0, 0);
+	fprintf(stderr, "%s\n", buf);
+#endif
 }
 
 int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h,
 		    struct set *set)
 {
-	struct nfnl_nft_set *nls;
+	struct 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);
+	nft_set_attr_set_u32(nls, NFT_SET_ATTR_FLAGS, set->flags);
+	nft_set_attr_set_u32(nls, NFT_SET_ATTR_KEY_TYPE,
+			     dtype_map_to_kernel(set->keytype));
+	nft_set_attr_set_u32(nls, NFT_SET_ATTR_KEY_LEN,
+			     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);
+		nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_TYPE,
+				     dtype_map_to_kernel(set->datatype));
+		nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_LEN,
+				     set->datalen / BITS_PER_BYTE);
 	}
-	netlink_dump_object(OBJ_CAST(nls));
-
-	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;
+	netlink_dump_set(nls);
 
+	err = mnl_nft_set_add(nf_sock, nls, NLM_F_EXCL | NLM_F_ECHO);
 	if (err < 0)
 		netlink_io_error(ctx, NULL, "Could not add set: %s",
-				 nl_geterror(err));
+				 strerror(errno));
+
+	set->handle.set =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
+	nft_set_free(nls);
+
 	return err;
 }
 
 int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h,
 		       const struct location *loc)
 {
-	struct nfnl_nft_set *nls;
+	struct nft_set *nls;
 	int err;
 
 	nls = alloc_nft_set(h);
-	err = nfnl_nft_set_delete(nf_sock, nls, 0);
-	nfnl_nft_set_put(nls);
+	err = mnl_nft_set_delete(nf_sock, nls, 0);
+	nft_set_free(nls);
 
 	if (err < 0)
 		netlink_io_error(ctx, loc, "Could not delete set: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
-static void list_set_cb(struct nl_object *obj, void *arg)
+static int list_set_cb(struct nft_set *nls, 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;
+	uint32_t flags, key, data;
 	struct set *set;
 
-	netlink_dump_object(obj);
-	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));
+	netlink_dump_set(nls);
+	key = nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_TYPE);
+	keytype = dtype_map_from_kernel(key);
 	if (keytype == NULL) {
 		netlink_io_error(ctx, NULL, "Unknown data type in set key %u",
-				 nfnl_nft_set_get_keytype(nls));
-		return;
+				 key);
+		return -1;
 	}
 
-	flags = nfnl_nft_set_get_flags(nls);
+	flags = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
 	if (flags & NFT_SET_MAP) {
-		datatype = dtype_map_from_kernel(nfnl_nft_set_get_datatype(nls));
+		data = nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_TYPE);
+		datatype = dtype_map_from_kernel(data);
 		if (datatype == NULL) {
 			netlink_io_error(ctx, NULL, "Unknown data type in set key %u",
-					 nfnl_nft_set_get_datatype(nls));
-			return;
+					 data);
+			return -1;
 		}
 	} 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->handle.family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY);
+	set->handle.table  =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE));
+	set->handle.set    =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
 	set->keytype       = keytype;
-	set->keylen        = nfnl_nft_set_get_keylen(nls) * BITS_PER_BYTE;
+	set->keylen        =
+		nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_LEN) * BITS_PER_BYTE;
 	set->flags         = flags;
 	set->datatype      = datatype;
-	set->datalen       = nfnl_nft_set_get_datalen(nls) * BITS_PER_BYTE;
+	if (nft_set_attr_is_set(nls, NFT_SET_ATTR_DATA_LEN)) {
+		set->datalen =
+			nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN) * BITS_PER_BYTE;
+	}
 	list_add_tail(&set->list, &ctx->list);
+
+	return 0;
 }
 
 int netlink_list_sets(struct netlink_ctx *ctx, const struct handle *h,
 		      const struct location *loc)
 {
-	struct nl_cache *set_cache;
-	int err;
+	struct nft_set_list *set_cache;
 
-	err = nfnl_nft_set_alloc_cache(nf_sock, h->family, h->table, &set_cache);
-	if (err < 0)
+	set_cache = mnl_nft_set_dump(nf_sock, h->family, h->table);
+	if (set_cache == NULL)
 		return netlink_io_error(ctx, loc,
 					"Could not receive sets from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
 
-	nl_cache_foreach(set_cache, list_set_cb, ctx);
-	nl_cache_free(set_cache);
+	nft_set_list_foreach(set_cache, list_set_cb, ctx);
+	nft_set_list_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,
 		    const struct location *loc)
 {
-	struct nfnl_nft_set *nls;
+	struct nft_set *nls;
+	struct set *set;
 	int err;
 
 	nls = alloc_nft_set(h);
-	netlink_dump_object(OBJ_CAST(nls));
-	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);
-
+	netlink_dump_set(nls);
+	err = mnl_nft_set_get(nf_sock, nls);
 	if (err < 0)
 		return netlink_io_error(ctx, loc,
 					"Could not receive set from kernel: %s",
-					nl_geterror(err));
+					strerror(errno));
+
+	set = set_alloc(&internal_location);
+	set->handle.family = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY);
+	set->handle.table  =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_TABLE));
+	set->handle.set    =
+		xstrdup(nft_set_attr_get_str(nls, NFT_SET_ATTR_NAME));
+	set->keytype       =
+		 dtype_map_from_kernel(nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_TYPE));
+	set->keylen        =
+		nft_set_attr_get_u32(nls, NFT_SET_ATTR_KEY_LEN) * BITS_PER_BYTE;
+	set->flags         = nft_set_attr_get_u32(nls, NFT_SET_ATTR_FLAGS);
+	set->datatype      =
+		dtype_map_from_kernel(nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_TYPE));
+	if (nft_set_attr_is_set(nls, NFT_SET_ATTR_DATA_LEN)) {
+		set->datalen =
+			nft_set_attr_get_u32(nls, NFT_SET_ATTR_DATA_LEN) * BITS_PER_BYTE;
+	}
+	list_add_tail(&set->list, &ctx->list);
+	nft_set_free(nls);
+
 	return err;
 }
 
-static int alloc_setelem_cache(const struct expr *set, struct nl_cache **res)
+static void alloc_setelem_cache(const struct expr *set, struct nft_set *nls)
 {
-	struct nfnl_nft_setelem *nlse;
-	struct nl_cache *elements;
+	struct nft_set_elem *nlse;
 	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);
-		netlink_dump_object(OBJ_CAST(nlse));
-		nl_cache_add(elements, OBJ_CAST(nlse));
+		nft_set_elem_add(nls, 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;
+	struct nft_set *nls;
 	int err;
 
 	nls = alloc_nft_set(h);
-	netlink_dump_object(OBJ_CAST(nls));
+	alloc_setelem_cache(expr, nls);
+	netlink_dump_set(nls);
 
-	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);
+	err = mnl_nft_setelem_add(nf_sock, nls, 0);
+	nft_set_free(nls);
 	if (err < 0)
 		netlink_io_error(ctx, &expr->location,
 				 "Could not add set elements: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	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;
+	struct nft_set *nls;
 	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);
+	alloc_setelem_cache(expr, nls);
+	netlink_dump_set(nls);
+
+	err = mnl_nft_setelem_delete(nf_sock, nls, 0);
+	nft_set_free(nls);
 	if (err < 0)
 		netlink_io_error(ctx, &expr->location,
 				 "Could not delete set elements: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
 
-static void list_setelem_cb(struct nl_object *obj, void *arg)
+static int list_setelem_cb(struct nft_set_elem *nlse, void *arg)
 {
-	struct nfnl_nft_setelem *nlse = nl_object_priv(obj);
-	struct nfnl_nft_data *nld;
+	struct nft_data_delinearize nld;
 	struct netlink_ctx *ctx = arg;
 	struct set *set = ctx->set;
 	struct expr *expr, *data;
-	uint32_t flags;
+	uint32_t flags = 0;
 
-	netlink_dump_object(obj);
-	if (!nfnl_nft_setelem_test_key(nlse)) {
-		netlink_io_error(ctx, NULL, "Incomplete set element received");
-		return;
-	}
+	nld.value =
+		nft_set_elem_attr_get(nlse, NFT_SET_ELEM_ATTR_KEY, &nld.len);
+	if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_FLAGS))
+		flags = nft_set_elem_attr_get_u32(nlse, NFT_SET_ELEM_ATTR_FLAGS);
 
-	nld   = nfnl_nft_setelem_get_key(nlse);
-	flags = nfnl_nft_setelem_get_flags(nlse);
-
-	expr = netlink_alloc_value(&internal_location, nld);
+	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)
+	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,
+	} else {
+		if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_DATA)) {
+			nld.value = nft_set_elem_attr_get(nlse, NFT_SET_ELEM_ATTR_DATA,
+							  &nld.len);
+		} else if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_CHAIN)) {
+			nld.chain = nft_set_elem_attr_get_str(nlse, NFT_SET_ELEM_ATTR_CHAIN);
+			nld.verdict = nft_set_elem_attr_get_u32(nlse, NFT_SET_ELEM_ATTR_VERDICT);
+		} else if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_VERDICT)) {
+			nld.verdict = nft_set_elem_attr_get_u32(nlse, NFT_SET_ELEM_ATTR_VERDICT);
+		} else
+			goto out;
+
+		data = netlink_alloc_data(&internal_location, &nld,
 					  set->datatype->type == TYPE_VERDICT ?
 					  NFT_REG_VERDICT : NFT_REG_1);
 		data->dtype = set->datatype;
 
 		expr = mapping_expr_alloc(&internal_location, expr, data);
 	}
-
+out:
 	compound_expr_add(set->init, expr);
+	return 0;
 }
 
 extern void interval_map_decompose(struct expr *set);
@@ -973,32 +970,27 @@ extern void interval_map_decompose(struct expr *set);
 int netlink_get_setelems(struct netlink_ctx *ctx, const struct handle *h,
 			 const struct location *loc, struct set *set)
 {
-	struct nl_cache *elements;
-	struct nfnl_nft_set *nls;
+	struct nft_set *nls;
 	int err;
 
 	nls = alloc_nft_set(h);
-	netlink_dump_object(OBJ_CAST(nls));
+	netlink_dump_set(nls);
 
-	err = nfnl_nft_setelem_alloc_cache(nf_sock, nls, &elements);
-	if (err < 0)
-		goto out;
-	err = nl_recvmsgs_default(nf_sock);
+	err = mnl_nft_setelem_get(nf_sock, nls);
 	if (err < 0)
 		goto out;
 
 	ctx->set = set;
 	set->init = set_expr_alloc(loc);
-	nl_cache_foreach(elements, list_setelem_cb, ctx);
-	nl_cache_free(elements);
+	nft_set_elem_foreach(nls, list_setelem_cb, ctx);
+	nft_set_free(nls);
 	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, loc, "Could not receive set elements: %s",
-				 nl_geterror(err));
+				 strerror(errno));
 	return err;
 }
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 50672b9..c24e105 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -8,6 +9,9 @@
  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  */
 
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
 #include <limits.h>
 #include <linux/netfilter/nf_tables.h>
 #include <netlink.h>
@@ -69,14 +73,22 @@ static struct expr *netlink_get_register(struct netlink_parse_ctx *ctx,
 
 static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
 				    const struct location *loc,
-				    const struct nfnl_nft_expr *nle)
+				    const struct nft_rule_expr *nle)
 {
-	const struct nfnl_nft_data *data = nfnl_nft_immediate_get_data(nle);
-	enum nft_registers dreg = nfnl_nft_immediate_get_dreg(nle);
+	struct nft_data_delinearize nld;
+	enum nft_registers dreg;
 	struct stmt *stmt;
 	struct expr *expr;
 
-	expr = netlink_alloc_data(loc, data, dreg);
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_IMM_VERDICT))
+		nld.verdict = nft_rule_expr_get_u32(nle, NFT_EXPR_IMM_VERDICT); 
+	else if (nft_rule_expr_is_set(nle, NFT_EXPR_IMM_DATA)) {
+		nld.value = nft_rule_expr_get(nle, NFT_EXPR_IMM_DATA, &nld.len);
+	}
+
+	dreg = nft_rule_expr_get_u32(nle, NFT_EXPR_IMM_DREG);
+
+	expr = netlink_alloc_data(loc, &nld, dreg);
 	if (dreg == NFT_REG_VERDICT) {
 		stmt = verdict_stmt_alloc(loc, expr);
 		list_add_tail(&stmt->list, &ctx->rule->stmts);
@@ -84,9 +96,9 @@ static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
 		netlink_set_register(ctx, dreg, expr);
 }
 
-static enum ops netlink_parse_cmp_op(const struct nfnl_nft_expr *nle)
+static enum ops netlink_parse_cmp_op(const struct nft_rule_expr *nle)
 {
-	switch (nfnl_nft_cmp_get_op(nle)) {
+	switch (nft_rule_expr_get_u8(nle, NFT_EXPR_CMP_OP)) {
 	case NFT_CMP_EQ:
 		return OP_EQ;
 	case NFT_CMP_NEQ:
@@ -106,21 +118,23 @@ static enum ops netlink_parse_cmp_op(const struct nfnl_nft_expr *nle)
 
 static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
 			      const struct location *loc,
-			      const struct nfnl_nft_expr *nle)
+			      const struct nft_rule_expr *nle)
 {
-	const struct nfnl_nft_data *data = nfnl_nft_cmp_get_data(nle);
+	struct nft_data_delinearize nld;
 	struct expr *expr, *left, *right;
 	struct stmt *stmt;
 	enum ops op;
 
-	left = netlink_get_register(ctx, loc, nfnl_nft_cmp_get_sreg(nle));
+	nld.value = nft_rule_expr_get(nle, NFT_EXPR_CMP_DATA, &nld.len);
+	left = netlink_get_register(ctx, loc,
+			nft_rule_expr_get_u8(nle, NFT_EXPR_CMP_SREG));
 	if (left == NULL)
 		return netlink_error(ctx, loc,
 				     "Relational expression has no left "
 				     "hand side");
 
 	op = netlink_parse_cmp_op(nle);
-	right = netlink_alloc_value(loc, data);
+	right = netlink_alloc_value(loc, &nld);
 
 	// FIXME
 	if (left->len && left->dtype && left->dtype->type != TYPE_STRING &&
@@ -135,28 +149,30 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
 
 static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,
 				 const struct location *loc,
-				 const struct nfnl_nft_expr *nle)
+				 const struct nft_rule_expr *nle)
 {
 	struct stmt *stmt;
 	struct expr *expr, *left, *right;
 	struct set *set;
 	enum nft_registers dreg;
 
-	left = netlink_get_register(ctx, loc, nfnl_nft_lookup_get_sreg(nle));
+	left = netlink_get_register(ctx, loc,
+			nft_rule_expr_get_u32(nle, NFT_EXPR_LOOKUP_SREG));
 	if (left == NULL)
 		return netlink_error(ctx, loc,
 				     "Lookup expression has no left hand side");
 
-	set = set_lookup(ctx->table, nfnl_nft_lookup_get_set(nle));
+	set = set_lookup(ctx->table,
+			 nft_rule_expr_get_str(nle, NFT_EXPR_LOOKUP_SET));
 	if (set == NULL)
 		return netlink_error(ctx, loc,
 				     "Unknown set '%s' in lookup expression",
-				     nfnl_nft_lookup_get_set(nle));
+				     nft_rule_expr_get_str(nle, NFT_EXPR_LOOKUP_SET));
 
 	right = set_ref_expr_alloc(loc, set);
 
-	if (nfnl_nft_lookup_test_dreg(nle)) {
-		dreg = nfnl_nft_lookup_get_dreg(nle);
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOOKUP_DREG)) {
+		dreg = nft_rule_expr_get_u32(nle, NFT_EXPR_LOOKUP_DREG);
 		expr = map_expr_alloc(loc, left, right);
 		if (dreg != NFT_REG_VERDICT)
 			return netlink_set_register(ctx, dreg, expr);
@@ -170,12 +186,14 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx,
 
 static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
 				  const struct location *loc,
-				  const struct nfnl_nft_expr *nle)
+				  const struct nft_rule_expr *nle)
 {
 	struct expr *expr, *left, *mask, *xor, *or;
 	mpz_t m, x, o;
+	struct nft_data_delinearize nld;
 
-	left = netlink_get_register(ctx, loc, nfnl_nft_bitwise_get_sreg(nle));
+	left = netlink_get_register(ctx, loc,
+			nft_rule_expr_get_u32(nle, NFT_EXPR_BITWISE_SREG));
 	if (left == NULL)
 		return netlink_error(ctx, loc,
 				     "Bitwise expression has no left "
@@ -183,10 +201,14 @@ static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
 
 	expr = left;
 
-	mask = netlink_alloc_value(loc, nfnl_nft_bitwise_get_mask(nle));
+	nld.value = nft_rule_expr_get(nle, NFT_EXPR_BITWISE_MASK, &nld.len);
+
+	mask = netlink_alloc_value(loc, &nld);
 	mpz_init_set(m, mask->value);
 
-	xor  = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle));
+	nld.value = nft_rule_expr_get(nle, NFT_EXPR_BITWISE_XOR, &nld.len);
+
+	xor  = netlink_alloc_value(loc, &nld);
 	mpz_init_set(x, xor->value);
 
 	mpz_init_set_ui(o, 0);
@@ -215,7 +237,10 @@ static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
 		expr_free(xor);
 
 	if (mpz_cmp_ui(o, 0)) {
-		or = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle));
+		nld.value = nft_rule_expr_get(nle, NFT_EXPR_BITWISE_XOR,
+					      &nld.len);
+
+		or = netlink_alloc_value(loc, &nld);
 		mpz_set(or->value, o);
 		expr = binop_expr_alloc(loc, OP_OR, expr, or);
 		expr->len = left->len;
@@ -225,23 +250,26 @@ static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
 	mpz_clear(x);
 	mpz_clear(o);
 
-	netlink_set_register(ctx, nfnl_nft_bitwise_get_dreg(nle), expr);
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u32(nle, NFT_EXPR_BITWISE_DREG),
+			     expr);
 }
 
 static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
 				    const struct location *loc,
-				    const struct nfnl_nft_expr *nle)
+				    const struct nft_rule_expr *nle)
 {
 	struct expr *expr, *arg;
 	enum ops op;
 
-	arg = netlink_get_register(ctx, loc, nfnl_nft_byteorder_get_sreg(nle));
+	arg = netlink_get_register(ctx, loc,
+		nft_rule_expr_get_u32(nle, NFT_EXPR_BYTEORDER_SREG));
 	if (arg == NULL)
 		return netlink_error(ctx, loc,
 				     "Byteorder expression has no left "
 				     "hand side");
 
-	switch (nfnl_nft_byteorder_get_op(nle)) {
+	switch (nft_rule_expr_get_u32(nle, NFT_EXPR_BYTEORDER_OP)) {
 	case NFT_BYTEORDER_NTOH:
 		op = OP_NTOH;
 		break;
@@ -250,106 +278,120 @@ static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
 		break;
 	default:
 		BUG("invalid byteorder operation %u\n",
-		    nfnl_nft_byteorder_get_op(nle));
+		    nft_rule_expr_get_u32(nle, NFT_EXPR_BYTEORDER_OP));
 	}
 
 	expr = unary_expr_alloc(loc, op, arg);
 	expr->len = arg->len;
-	netlink_set_register(ctx, nfnl_nft_byteorder_get_dreg(nle), expr);
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u32(nle, NFT_EXPR_BYTEORDER_DREG),
+			     expr);
 }
 
 static void netlink_parse_payload(struct netlink_parse_ctx *ctx,
 				  const struct location *loc,
-				  const struct nfnl_nft_expr *nle)
+				  const struct nft_rule_expr *nle)
 {
 	struct expr *expr;
 
 	expr = payload_expr_alloc(loc, NULL, 0);
-	payload_init_raw(expr, nfnl_nft_payload_get_base(nle) + 1,
-			 nfnl_nft_payload_get_offset(nle) * BITS_PER_BYTE,
-			 nfnl_nft_payload_get_len(nle) * BITS_PER_BYTE);
+	payload_init_raw(expr, nft_rule_expr_get_u32(nle, NFT_EXPR_PAYLOAD_BASE) + 1,
+			 nft_rule_expr_get_u32(nle, NFT_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE,
+			 nft_rule_expr_get_u32(nle, NFT_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE);
 
-	netlink_set_register(ctx, nfnl_nft_payload_get_dreg(nle), expr);
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u32(nle, NFT_EXPR_PAYLOAD_DREG),
+			     expr);
 }
 
 static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
 				 const struct location *loc,
-				 const struct nfnl_nft_expr *nle)
+				 const struct nft_rule_expr *nle)
 {
 	struct expr *expr;
 
 	expr = exthdr_expr_alloc(loc, NULL, 0);
-	exthdr_init_raw(expr, nfnl_nft_exthdr_get_type(nle),
-			nfnl_nft_exthdr_get_offset(nle) * BITS_PER_BYTE,
-			nfnl_nft_exthdr_get_len(nle) * BITS_PER_BYTE);
+	exthdr_init_raw(expr, nft_rule_expr_get_u8(nle, NFT_EXPR_EXTHDR_TYPE),
+			nft_rule_expr_get_u32(nle, NFT_EXPR_EXTHDR_OFFSET) * BITS_PER_BYTE,
+			nft_rule_expr_get_u32(nle, NFT_EXPR_EXTHDR_LEN) * BITS_PER_BYTE);
 
-	netlink_set_register(ctx, nfnl_nft_exthdr_get_dreg(nle), expr);
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u32(nle, NFT_EXPR_EXTHDR_DREG),
+			     expr);
 }
 
 static void netlink_parse_meta(struct netlink_parse_ctx *ctx,
 			       const struct location *loc,
-			       const struct nfnl_nft_expr *nle)
+			       const struct nft_rule_expr *nle)
 {
 	struct expr *expr;
 
-	expr = meta_expr_alloc(loc, nfnl_nft_meta_get_key(nle));
-	netlink_set_register(ctx, nfnl_nft_meta_get_dreg(nle), expr);
+	expr = meta_expr_alloc(loc,
+			       nft_rule_expr_get_u8(nle, NFT_EXPR_META_KEY));
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u8(nle, NFT_EXPR_META_DREG),
+			     expr);
 }
 
 static void netlink_parse_ct(struct netlink_parse_ctx *ctx,
 			     const struct location *loc,
-			     const struct nfnl_nft_expr *nle)
+			     const struct nft_rule_expr *nle)
 {
 	struct expr *expr;
 
-	expr = ct_expr_alloc(loc, nfnl_nft_ct_get_key(nle));
-	netlink_set_register(ctx, nfnl_nft_ct_get_dreg(nle), expr);
+	expr = ct_expr_alloc(loc, nft_rule_expr_get_u32(nle, NFT_EXPR_CT_KEY));
+	netlink_set_register(ctx,
+			     nft_rule_expr_get_u32(nle, NFT_EXPR_CT_DREG),
+			     expr);
 }
 
 static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
 				  const struct location *loc,
-				  const struct nfnl_nft_expr *nle)
+				  const struct nft_rule_expr *nle)
 {
 	struct stmt *stmt;
 
 	stmt = counter_stmt_alloc(loc);
-	stmt->counter.packets = nfnl_nft_counter_get_packets(nle);
-	stmt->counter.bytes   = nfnl_nft_counter_get_bytes(nle);
+	stmt->counter.packets =
+		nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_PACKETS);
+	stmt->counter.bytes   =
+		nft_rule_expr_get_u64(nle, NFT_EXPR_CTR_BYTES);
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
 static void netlink_parse_log(struct netlink_parse_ctx *ctx,
 			      const struct location *loc,
-			      const struct nfnl_nft_expr *nle)
+			      const struct nft_rule_expr *nle)
 {
 	struct stmt *stmt;
 	const char *prefix;
 
 	stmt = log_stmt_alloc(loc);
-	prefix = nfnl_nft_log_get_prefix(nle);
+	prefix = nft_rule_expr_get_str(nle, NFT_EXPR_LOG_PREFIX);
 	if (prefix != NULL)
 		stmt->log.prefix = xstrdup(prefix);
-	stmt->log.group	     = nfnl_nft_log_get_group(nle);
-	stmt->log.snaplen    = nfnl_nft_log_get_snaplen(nle);
-	stmt->log.qthreshold = nfnl_nft_log_get_qthreshold(nle);
+	stmt->log.group = nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_GROUP);
+	stmt->log.snaplen = nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_SNAPLEN);
+	stmt->log.qthreshold =
+		nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_QTHRESHOLD);
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
 static void netlink_parse_limit(struct netlink_parse_ctx *ctx,
 				const struct location *loc,
-				const struct nfnl_nft_expr *nle)
+				const struct nft_rule_expr *nle)
 {
 	struct stmt *stmt;
 
 	stmt = limit_stmt_alloc(loc);
-	stmt->limit.rate  = nfnl_nft_limit_get_rate(nle);
-	stmt->limit.depth = nfnl_nft_limit_get_depth(nle);
+	stmt->limit.rate  = nft_rule_expr_get_u32(nle, NFT_EXPR_LIMIT_RATE);
+	stmt->limit.depth  = nft_rule_expr_get_u32(nle, NFT_EXPR_LIMIT_DEPTH);
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
 static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
 				 const struct location *loc,
-				 const struct nfnl_nft_expr *expr)
+				 const struct nft_rule_expr *expr)
 {
 	struct stmt *stmt;
 
@@ -359,16 +401,16 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
 
 static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
 			      const struct location *loc,
-			      const struct nfnl_nft_expr *nle)
+			      const struct nft_rule_expr *nle)
 {
 	struct stmt *stmt;
 	struct expr *addr, *proto;
 	enum nft_registers reg1, reg2;
 
 	stmt = nat_stmt_alloc(loc);
-	stmt->nat.type = nfnl_nft_nat_get_type(nle);
+	stmt->nat.type = nft_rule_expr_get_u32(nle, NFT_EXPR_NAT_TYPE);
 
-	reg1 = nfnl_nft_nat_get_sreg_addr_min(nle);
+	reg1 = nft_rule_expr_get_u32(nle, NFT_EXPR_NAT_REG_ADDR_MIN);
 	if (reg1) {
 		addr = netlink_get_register(ctx, loc, reg1);
 		if (addr == NULL)
@@ -380,7 +422,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
 		stmt->nat.addr = addr;
 	}
 
-	reg2 = nfnl_nft_nat_get_sreg_addr_max(nle);
+	reg2 = nft_rule_expr_get_u32(nle, NFT_EXPR_NAT_REG_ADDR_MAX);
 	if (reg2 && reg2 != reg1) {
 		addr = netlink_get_register(ctx, loc, reg2);
 		if (addr == NULL)
@@ -394,7 +436,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
 		stmt->nat.addr = addr;
 	}
 
-	reg1 = nfnl_nft_nat_get_sreg_proto_min(nle);
+	reg1 = nft_rule_expr_get_u32(nle, NFT_EXPR_NAT_REG_PROTO_MIN);
 	if (reg1) {
 		proto = netlink_get_register(ctx, loc, reg1);
 		if (proto == NULL)
@@ -406,7 +448,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
 		stmt->nat.proto = proto;
 	}
 
-	reg2 = nfnl_nft_nat_get_sreg_proto_max(nle);
+	reg2 = nft_rule_expr_get_u32(nle, NFT_EXPR_NAT_REG_PROTO_MAX);
 	if (reg2 && reg2 != reg1) {
 		proto = netlink_get_register(ctx, loc, reg2);
 		if (proto == NULL)
@@ -427,7 +469,7 @@ static const struct {
 	const char	*name;
 	void		(*parse)(struct netlink_parse_ctx *ctx,
 				 const struct location *loc,
-				 const struct nfnl_nft_expr *nle);
+				 const struct nft_rule_expr *nle);
 } netlink_parsers[] = {
 	{ .name = "immediate",	.parse = netlink_parse_immediate },
 	{ .name = "cmp",	.parse = netlink_parse_cmp },
@@ -450,25 +492,26 @@ static const struct input_descriptor indesc_netlink = {
 	.type  = INDESC_NETLINK,
 };
 
-static void netlink_parse_expr(struct nl_object *obj, void *arg)
+static int netlink_parse_expr(struct nft_rule_expr *nle, void *arg)
 {
-	const struct nfnl_nft_expr *nle = (struct nfnl_nft_expr *)obj;
-	const char *type = nfnl_nft_expr_get_type(nle);
+	const char *type = nft_rule_expr_get_str(nle, NFT_RULE_EXPR_ATTR_NAME);
 	struct netlink_parse_ctx *ctx = arg;
 	struct location loc;
 	unsigned int i;
 
 	memset(&loc, 0, sizeof(loc));
 	loc.indesc = &indesc_netlink;
-	loc.nl_obj = obj;
+	loc.nle = nle;
 
 	for (i = 0; i < array_size(netlink_parsers); i++) {
 		if (strcmp(type, netlink_parsers[i].name))
 			continue;
-		return netlink_parsers[i].parse(ctx, &loc, nle);
+		netlink_parsers[i].parse(ctx, &loc, nle);
+		return 0;
 	}
 
 	netlink_error(ctx, &loc, "unknown expression type '%s'", type);
+	return 0;
 }
 
 struct rule_pp_ctx {
@@ -697,9 +740,8 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
 }
 
 struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
-				      const struct nl_object *obj)
+				      const struct nft_rule *nlr)
 {
-	const struct nfnl_nft_rule *nlr = (const struct nfnl_nft_rule *)obj;
 	struct netlink_parse_ctx _ctx, *pctx = &_ctx;
 	struct handle h;
 
@@ -707,15 +749,15 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
 	_ctx.msgs = ctx->msgs;
 
 	memset(&h, 0, sizeof(h));
-	h.family = nfnl_nft_rule_get_family(nlr);
-	h.table  = xstrdup(nfnl_nft_rule_get_table(nlr));
-	h.chain  = xstrdup(nfnl_nft_rule_get_chain(nlr));
-	h.handle = nfnl_nft_rule_get_handle(nlr);
+	h.family = nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY);
+	h.table  = xstrdup(nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_TABLE));
+	h.chain  = xstrdup(nft_rule_attr_get_str(nlr, NFT_RULE_ATTR_CHAIN));
+	h.handle = nft_rule_attr_get_u64(nlr, NFT_RULE_ATTR_HANDLE);
 
 	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);
+	nft_rule_expr_foreach((struct nft_rule *)nlr, netlink_parse_expr, pctx);
 
 	rule_parse_postprocess(pctx, pctx->rule);
 	return pctx->rule;
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index accab9c..9929f4a 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -10,6 +11,7 @@
 
 #include <linux/netfilter/nf_tables.h>
 
+#include <string.h>
 #include <rule.h>
 #include <statement.h>
 #include <expression.h>
@@ -18,7 +20,7 @@
 #include <utils.h>
 
 struct netlink_linearize_ctx {
-	struct nfnl_nft_rule	*nlr;
+	struct nft_rule		*nlr;
 	unsigned int		reg_low;
 };
 
@@ -52,59 +54,65 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
 				const struct expr *expr,
 				enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
-
-	nle = alloc_nft_expr(nfnl_nft_payload_init);
-	nfnl_nft_payload_set_dreg(nle, dreg);
-	nfnl_nft_payload_set_base(nle, expr->payload.base - 1);
-	nfnl_nft_payload_set_offset(nle, expr->payload.offset / BITS_PER_BYTE);
-	nfnl_nft_payload_set_len(nle, expr->len / BITS_PER_BYTE);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	struct nft_rule_expr *nle;
+
+	nle = alloc_nft_expr("payload");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_PAYLOAD_DREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_PAYLOAD_BASE,
+			      expr->payload.base - 1);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_PAYLOAD_OFFSET,
+			      expr->payload.offset / BITS_PER_BYTE);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_PAYLOAD_LEN,
+			      expr->len / BITS_PER_BYTE);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
 			       const struct expr *expr,
 			       enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
-
-	nle = alloc_nft_expr(nfnl_nft_exthdr_init);
-	nfnl_nft_exthdr_set_dreg(nle, dreg);
-	nfnl_nft_exthdr_set_type(nle, expr->exthdr.desc->type);
-	nfnl_nft_exthdr_set_offset(nle, expr->exthdr.tmpl->offset / BITS_PER_BYTE);
-	nfnl_nft_exthdr_set_len(nle, expr->len / BITS_PER_BYTE);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	struct nft_rule_expr *nle;
+
+	nle = alloc_nft_expr("exthdr");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_EXTHDR_DREG, dreg);
+	nft_rule_expr_set_u8(nle, NFT_EXPR_EXTHDR_TYPE,
+			     expr->exthdr.desc->type);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_EXTHDR_OFFSET,
+			      expr->exthdr.tmpl->offset / BITS_PER_BYTE);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_EXTHDR_LEN,
+			      expr->len / BITS_PER_BYTE);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
 			     const struct expr *expr,
 			     enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
-	nle = alloc_nft_expr(nfnl_nft_meta_init);
-	nfnl_nft_meta_set_dreg(nle, dreg);
-	nfnl_nft_meta_set_key(nle, expr->meta.key);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("meta");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_META_DREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_META_KEY, expr->meta.key);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
 			   const struct expr *expr,
 			   enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
-	nle = alloc_nft_expr(nfnl_nft_ct_init);
-	nfnl_nft_ct_set_dreg(nle, dreg);
-	nfnl_nft_ct_set_key(nle, expr->ct.key);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("ct");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_CT_DREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_CT_KEY, expr->ct.key);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
 			    const struct expr *expr,
 			    enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers sreg;
 
 	assert(expr->mappings->ops->type == EXPR_SET_REF);
@@ -116,22 +124,23 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
 
 	netlink_gen_expr(ctx, expr->map, sreg);
 
-	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);
+	nle = alloc_nft_expr("lookup");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SREG, sreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_DREG, dreg);
+	nft_rule_expr_set_str(nle, NFT_EXPR_LOOKUP_SET,
+			      expr->mappings->set->handle.set);
 
 	if (dreg == NFT_REG_VERDICT)
 		release_register(ctx);
 
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
 			       const struct expr *expr,
 			       enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers sreg;
 
 	assert(expr->right->ops->type == EXPR_SET_REF);
@@ -140,12 +149,13 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
 	sreg = get_register(ctx);
 	netlink_gen_expr(ctx, expr->left, sreg);
 
-	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);
+	nle = alloc_nft_expr("lookup");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SREG, sreg);
+	nft_rule_expr_set_str(nle, NFT_EXPR_LOOKUP_SET,
+			      expr->right->set->handle.set);
 
 	release_register(ctx);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op)
@@ -172,21 +182,24 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx,
 			    const struct expr *expr,
 			    enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers sreg;
+	struct nft_data_linearize nld;
 
 	assert(dreg == NFT_REG_VERDICT);
 
 	sreg = get_register(ctx);
 	netlink_gen_expr(ctx, expr->left, sreg);
 
-	nle = alloc_nft_expr(nfnl_nft_cmp_init);
-	nfnl_nft_cmp_set_sreg(nle, sreg);
-	nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(expr->op));
-	nfnl_nft_cmp_set_data(nle, netlink_gen_data(expr->right));
+	nle = alloc_nft_expr("cmp");
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_SREG, sreg);
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_OP,
+			      netlink_gen_cmp_op(expr->op));
+	netlink_gen_data(expr->right, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len);
 	release_register(ctx);
 
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
@@ -194,25 +207,30 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
 			      enum nft_registers dreg)
 {
 	struct expr *range = expr->right;
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers sreg;
+	struct nft_data_linearize nld;
 
 	assert(dreg == NFT_REG_VERDICT);
 
 	sreg = get_register(ctx);
 	netlink_gen_expr(ctx, expr->left, sreg);
 
-	nle = alloc_nft_expr(nfnl_nft_cmp_init);
-	nfnl_nft_cmp_set_sreg(nle, sreg);
-	nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(OP_GTE));
-	nfnl_nft_cmp_set_data(nle, netlink_gen_data(range->left));
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
-
-	nle = alloc_nft_expr(nfnl_nft_cmp_init);
-	nfnl_nft_cmp_set_sreg(nle, sreg);
-	nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(OP_LTE));
-	nfnl_nft_cmp_set_data(nle, netlink_gen_data(range->right));
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("cmp");
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_SREG, sreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_CMP_OP,
+			      netlink_gen_cmp_op(OP_GTE));
+	netlink_gen_data(range->left, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len);
+	nft_rule_add_expr(ctx->nlr, nle);
+
+	nle = alloc_nft_expr("cmp");
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_SREG, sreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_CMP_OP,
+			      netlink_gen_cmp_op(OP_LTE));
+	netlink_gen_data(range->right, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len);
+	nft_rule_add_expr(ctx->nlr, nle);
 
 	release_register(ctx);
 }
@@ -221,8 +239,8 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
 				const struct expr *expr,
 				enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
-	struct nfnl_nft_data *nld;
+	struct nft_rule_expr *nle;
+	struct nft_data_linearize nld, nld2;
 	enum nft_registers sreg;
 	unsigned int len;
 	mpz_t zero;
@@ -235,21 +253,23 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
 
 	mpz_init_set_ui(zero, 0);
 
-	nle = alloc_nft_expr(nfnl_nft_bitwise_init);
-	nld = netlink_gen_raw_data(zero, expr->right->byteorder, len);
-	nfnl_nft_bitwise_set_sreg(nle, sreg);
-	nfnl_nft_bitwise_set_dreg(nle, sreg);
-	nfnl_nft_bitwise_set_len(nle, len);
-	nfnl_nft_bitwise_set_mask(nle, netlink_gen_data(expr->right));
-	nfnl_nft_bitwise_set_xor(nle, nld);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
-
-	nle = alloc_nft_expr(nfnl_nft_cmp_init);
-	nld = netlink_gen_raw_data(zero, expr->right->byteorder, len);
-	nfnl_nft_cmp_set_sreg(nle, sreg);
-	nfnl_nft_cmp_set_op(nle, NFT_CMP_NEQ);
-	nfnl_nft_cmp_set_data(nle, nld);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("bitwise");
+	netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_SREG, sreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_DREG, sreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_LEN, len);
+	netlink_gen_data(expr->right, &nld2);
+	nft_rule_expr_set(nle, NFT_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
+	nft_rule_expr_set(nle, NFT_EXPR_BITWISE_XOR, &nld.value, nld.len);
+	nft_rule_add_expr(ctx->nlr, nle);
+
+	nle = alloc_nft_expr("cmp");
+	netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld);
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_SREG, sreg);
+	nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_OP, NFT_CMP_NEQ);
+	netlink_gen_data(expr->right, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len);
+	nft_rule_add_expr(ctx->nlr, nle);
 
 	mpz_clear(zero);
 	release_register(ctx);
@@ -291,8 +311,8 @@ static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
 			      const struct expr *expr,
 			      enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
-	struct nfnl_nft_data *nld;
+	struct nft_rule_expr *nle;
+	struct nft_data_linearize nld;
 	struct expr *left, *i;
 	struct expr *binops[16];
 	mpz_t mask, xor, val, tmp;
@@ -337,23 +357,22 @@ static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
 
 	len = div_round_up(expr->len, BITS_PER_BYTE);
 
-	nle = alloc_nft_expr(nfnl_nft_bitwise_init);
-	nfnl_nft_bitwise_set_sreg(nle, dreg);
-	nfnl_nft_bitwise_set_dreg(nle, dreg);
-	nfnl_nft_bitwise_set_len(nle, len);
-
-	nld = netlink_gen_raw_data(mask, expr->byteorder, len);
-	nfnl_nft_bitwise_set_mask(nle, nld);
+	nle = alloc_nft_expr("bitwise");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_SREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_DREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_LEN, len);
 
-	nld = netlink_gen_raw_data(xor, expr->byteorder, len);
-	nfnl_nft_bitwise_set_xor(nle, nld);
+	netlink_gen_raw_data(mask, expr->byteorder, len, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_BITWISE_MASK, nld.value, nld.len);
+	netlink_gen_raw_data(xor, expr->byteorder, len, &nld);
+	nft_rule_expr_set(nle, NFT_EXPR_BITWISE_XOR, nld.value, nld.len);
 
 	mpz_clear(tmp);
 	mpz_clear(val);
 	mpz_clear(xor);
 	mpz_clear(mask);
 
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static enum nft_byteorder_ops netlink_gen_unary_op(enum ops op)
@@ -372,29 +391,47 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
 			      const struct expr *expr,
 			      enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
 	netlink_gen_expr(ctx, expr->arg, dreg);
 
-	nle = alloc_nft_expr(nfnl_nft_byteorder_init);
-	nfnl_nft_byteorder_set_sreg(nle, dreg);
-	nfnl_nft_byteorder_set_dreg(nle, dreg);
-	nfnl_nft_byteorder_set_len(nle, expr->len / BITS_PER_BYTE);
-	nfnl_nft_byteorder_set_size(nle, expr->arg->len % 32 ? 2 : 4);
-	nfnl_nft_byteorder_set_op(nle, netlink_gen_unary_op(expr->op));
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("byteorder");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BYTEORDER_SREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BYTEORDER_DREG, dreg);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BYTEORDER_LEN,
+			      expr->len / BITS_PER_BYTE);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BYTEORDER_SIZE,
+			      expr->arg->len % 32 ? 2 : 4);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_BYTEORDER_OP,
+			      netlink_gen_unary_op(expr->op));
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
 				  const struct expr *expr,
 				  enum nft_registers dreg)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
+	struct nft_data_linearize nld;
 
-	nle = alloc_nft_expr(nfnl_nft_immediate_init);
-	nfnl_nft_immediate_set_dreg(nle, dreg);
-	nfnl_nft_immediate_set_data(nle, netlink_gen_data(expr));
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("immediate");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_IMM_DREG, dreg);
+	netlink_gen_data(expr, &nld);
+	switch (expr->ops->type) {
+	case EXPR_VALUE:
+		nft_rule_expr_set(nle, NFT_EXPR_IMM_DATA, nld.value, nld.len);
+		break;
+	case EXPR_VERDICT:
+		if (nft_rule_expr_is_set(nle, NFT_EXPR_IMM_CHAIN)) {
+			nft_rule_expr_set_str(nle, NFT_EXPR_IMM_CHAIN,
+					      nld.chain);
+		}
+		nft_rule_expr_set_u32(nle, NFT_EXPR_IMM_VERDICT, nld.verdict);
+		break;
+	default:
+		break;
+	}
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
@@ -437,77 +474,91 @@ static void netlink_gen_verdict_stmt(struct netlink_linearize_ctx *ctx,
 static void netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
 				     const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
-
-	nle = alloc_nft_expr(nfnl_nft_counter_init);
-	if (stmt->counter.packets)
-		nfnl_nft_counter_set_packets(nle, stmt->counter.packets);
-	if (stmt->counter.bytes)
-		nfnl_nft_counter_set_bytes(nle, stmt->counter.bytes);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	struct nft_rule_expr *nle;
+
+	nle = alloc_nft_expr("counter");
+	if (stmt->counter.packets) {
+		nft_rule_expr_set_u64(nle, NFT_EXPR_CTR_PACKETS,
+				      stmt->counter.packets);
+	}
+	if (stmt->counter.bytes) {
+		nft_rule_expr_set_u64(nle, NFT_EXPR_CTR_BYTES,
+				      stmt->counter.packets);
+	}
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
 				  const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers sreg;
 
 	sreg = get_register(ctx);
 	netlink_gen_expr(ctx, stmt->meta.expr, sreg);
 	release_register(ctx);
 
-	nle = alloc_nft_expr(nfnl_nft_meta_init);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("meta");
+	/* XXX nothing here ? */
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
 				 const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
-
-	nle = alloc_nft_expr(nfnl_nft_log_init);
-	if (stmt->log.prefix != NULL)
-		nfnl_nft_log_set_prefix(nle, stmt->log.prefix);
-	if (stmt->log.group)
-		nfnl_nft_log_set_group(nle, stmt->log.group);
-	if (stmt->log.snaplen)
-		nfnl_nft_log_set_snaplen(nle, stmt->log.snaplen);
-	if (stmt->log.qthreshold)
-		nfnl_nft_log_set_qthreshold(nle, stmt->log.qthreshold);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	struct nft_rule_expr *nle;
+
+	nle = alloc_nft_expr("log");
+	if (stmt->log.prefix != NULL) {
+		nft_rule_expr_set_str(nle, NFT_EXPR_LOG_PREFIX,
+				      stmt->log.prefix);
+	}
+	if (stmt->log.group) {
+		nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_GROUP,
+				      stmt->log.group);
+	}
+	if (stmt->log.snaplen) {
+		nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_SNAPLEN,
+				      stmt->log.snaplen);
+	}
+	if (stmt->log.qthreshold) {
+		nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_QTHRESHOLD,
+				      stmt->log.qthreshold);
+	}
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
 				   const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
-	nle = alloc_nft_expr(nfnl_nft_limit_init);
-	nfnl_nft_limit_set_rate(nle, stmt->limit.rate);
-	nfnl_nft_limit_set_depth(nle, stmt->limit.depth);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nle = alloc_nft_expr("limit");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_LIMIT_RATE, stmt->limit.rate);
+	nft_rule_expr_set_u32(nle, NFT_EXPR_LIMIT_DEPTH, stmt->limit.depth);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
 				    const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 
 	nle = alloc_nft_expr(NULL);
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	/* XXX nothing here? */
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
 				 const struct stmt *stmt)
 {
-	struct nfnl_nft_expr *nle;
+	struct nft_rule_expr *nle;
 	enum nft_registers amin_reg, amax_reg;
 	enum nft_registers pmin_reg, pmax_reg;
 	int registers = 0;
 
-	nle = alloc_nft_expr(nfnl_nft_nat_init);
-	nfnl_nft_nat_set_type(nle, stmt->nat.type);
+	nle = alloc_nft_expr("nat");
+	nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_TYPE, stmt->nat.type);
 
 	if (stmt->nat.addr) {
 		amin_reg = get_register(ctx);
@@ -519,11 +570,14 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
 
 			netlink_gen_expr(ctx, stmt->nat.addr->left, amin_reg);
 			netlink_gen_expr(ctx, stmt->nat.addr->right, amax_reg);
-			nfnl_nft_nat_set_sreg_addr_min(nle, amin_reg);
-			nfnl_nft_nat_set_sreg_addr_max(nle, amax_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_ADDR_MIN,
+					      amin_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_ADDR_MAX,
+					      amax_reg);
 		} else {
 			netlink_gen_expr(ctx, stmt->nat.addr, amin_reg);
-			nfnl_nft_nat_set_sreg_addr_min(nle, amin_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_ADDR_MIN,
+					      amin_reg);
 		}
 
 	}
@@ -538,11 +592,14 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
 
 			netlink_gen_expr(ctx, stmt->nat.proto->left, pmin_reg);
 			netlink_gen_expr(ctx, stmt->nat.proto->right, pmax_reg);
-			nfnl_nft_nat_set_sreg_proto_min(nle, pmin_reg);
-			nfnl_nft_nat_set_sreg_proto_max(nle, pmax_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_PROTO_MIN,
+					      pmin_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_PROTO_MAX,
+					      pmax_reg);
 		} else {
 			netlink_gen_expr(ctx, stmt->nat.proto, pmin_reg);
-			nfnl_nft_nat_set_sreg_proto_min(nle, pmin_reg);
+			nft_rule_expr_set_u32(nle, NFT_EXPR_NAT_REG_PROTO_MIN,
+					      pmin_reg);
 		}
 	}
 
@@ -551,7 +608,7 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
 		registers--;
 	}
 
-	nfnl_nft_rule_add_expr(ctx->nlr, nle);
+	nft_rule_add_expr(ctx->nlr, nle);
 }
 
 static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
@@ -579,7 +636,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
 	}
 }
 
-int netlink_linearize_rule(struct netlink_ctx *ctx, struct nfnl_nft_rule *nlr,
+int netlink_linearize_rule(struct netlink_ctx *ctx, struct nft_rule *nlr,
 			   const struct rule *rule)
 {
 	struct netlink_linearize_ctx lctx;
@@ -592,6 +649,6 @@ int netlink_linearize_rule(struct netlink_ctx *ctx, struct nfnl_nft_rule *nlr,
 	list_for_each_entry(stmt, &rule->stmts, list)
 		netlink_gen_stmt(&lctx, stmt);
 
-	netlink_dump_object(OBJ_CAST(nlr));
+	netlink_dump_rule(nlr);
 	return 0;
 }
