diff mbox

[libnftables] examples: nft-rule-add: use existing batch infrastructure

Message ID 1386695918-3623-1-git-send-email-pablo@netfilter.org
State Accepted
Headers show

Commit Message

Pablo Neira Ayuso Dec. 10, 2013, 5:18 p.m. UTC
From: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>

This patch reworks the existing example to add the rule:

  nft add rule ip filter input tcp dport 22 counter

It uses the existing nfnl batching approach using the generic mnl
netlink message batching infrastructure. It also removed the code
that uses xtables compat code.

Based on original patch by Arturo Borrero Gonzalez.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 examples/nft-rule-add.c             |  257 ++++++++++++++++++++---------------
 include/linux/netfilter/nfnetlink.h |    5 +
 2 files changed, 152 insertions(+), 110 deletions(-)

Comments

Eric Leblond Jan. 1, 2014, 10:39 p.m. UTC | #1
Hello,

On Tue, 2013-12-10 at 18:18 +0100, Pablo Neira Ayuso wrote:
> From: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> 
> This patch reworks the existing example to add the rule:
> 
>   nft add rule ip filter input tcp dport 22 counter
> 
> It uses the existing nfnl batching approach using the generic mnl
> netlink message batching infrastructure. It also removed the code
> that uses xtables compat code.

I don't see this patch in any of the branch. Is there any issue with
it ?

It seems really useful as existing examples are using really old code.

BR,

> 
> Based on original patch by Arturo Borrero Gonzalez.
> 
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
>  examples/nft-rule-add.c             |  257 ++++++++++++++++++++---------------
>  include/linux/netfilter/nfnetlink.h |    5 +
>  2 files changed, 152 insertions(+), 110 deletions(-)
> 
> diff --git a/examples/nft-rule-add.c b/examples/nft-rule-add.c
> index f896bc0..0534fa5 100644
> --- a/examples/nft-rule-add.c
> +++ b/examples/nft-rule-add.c
> @@ -14,126 +14,178 @@
>  #include <string.h>
>  #include <stddef.h>	/* for offsetof */
>  #include <netinet/in.h>
> +#include <netinet/ip.h>
> +#include <netinet/tcp.h>
>  #include <arpa/inet.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <errno.h>
>  
>  #include <linux/netfilter.h>
> +#include <linux/netfilter/nfnetlink.h>
>  #include <linux/netfilter/nf_tables.h>
>  
>  #include <libmnl/libmnl.h>
>  #include <libnftables/rule.h>
>  #include <libnftables/expr.h>
>  
> -#include <linux/netfilter_ipv4/ipt_LOG.h>
> -#include <linux/netfilter/xt_iprange.h>
> -
> -#include <netinet/ip.h>
> -
> -static void add_target_log(struct nft_rule_expr *e)
> +static void add_payload(struct nft_rule *r, uint32_t base, uint32_t dreg,
> +			uint32_t offset, uint32_t len)
>  {
> -	struct ipt_log_info *info;
> +	struct nft_rule_expr *e;
>  
> -	nft_rule_expr_set(e, NFT_EXPR_TG_NAME, "LOG", strlen("LOG"));
> -	nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, 0);
> -
> -	info = calloc(1, sizeof(struct ipt_log_info));
> -	if (info == NULL)
> -		return;
> +	e = nft_rule_expr_alloc("payload");
> +	if (e == NULL) {
> +		perror("expr payload oom");
> +		exit(EXIT_FAILURE);
> +	}
>  
> -	sprintf(info->prefix, "test: ");
> -	info->prefix[sizeof(info->prefix)-1] = '\0';
> -	info->logflags = 0x0f;
> -	info->level = 5;
> +	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base);
> +	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, dreg);
> +	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, offset);
> +	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, len);
>  
> -	nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, sizeof(*info));
> +	nft_rule_add_expr(r, e);
>  }
>  
> -static void add_expr_target(struct nft_rule *r)
> +static void add_cmp(struct nft_rule *r, uint32_t sreg, uint32_t op,
> +		    const void *data, uint32_t data_len)
>  {
> -	struct nft_rule_expr *expr;
> +	struct nft_rule_expr *e;
>  
> -	expr = nft_rule_expr_alloc("target");
> -	if (expr == NULL)
> -		return;
> +	e = nft_rule_expr_alloc("cmp");
> +	if (e == NULL) {
> +		perror("expr cmp oom");
> +		exit(EXIT_FAILURE);
> +	}
>  
> -	add_target_log(expr);
> +	nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, sreg);
> +	nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, op);
> +	nft_rule_expr_set(e, NFT_EXPR_CMP_DATA, data, data_len);
>  
> -	nft_rule_add_expr(r, expr);
> +	nft_rule_add_expr(r, e);
>  }
>  
> -static void add_match_iprange(struct nft_rule_expr *e)
> +static void add_counter(struct nft_rule *r)
>  {
> -	struct xt_iprange_mtinfo *info;
> -
> -	nft_rule_expr_set(e, NFT_EXPR_MT_NAME, "iprange", strlen("iprange"));
> -	nft_rule_expr_set_u32(e, NFT_EXPR_MT_REV, 1);
> +	struct nft_rule_expr *e;
>  
> -	info = calloc(1, sizeof(struct xt_iprange_mtinfo));
> -	if (info == NULL)
> -		return;
> -
> -	info->src_min.ip = info->dst_min.ip = inet_addr("127.0.0.1");
> -	info->src_max.ip = info->dst_max.ip = inet_addr("127.0.0.1");
> -	info->flags = IPRANGE_SRC;
> +	e = nft_rule_expr_alloc("counter");
> +	if (e == NULL) {
> +		perror("expr counter oom");
> +		exit(EXIT_FAILURE);
> +	}
>  
> -	nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, sizeof(*info));
> +	nft_rule_add_expr(r, e);
>  }
>  
> -static void add_expr_match(struct nft_rule *r)
> +static struct nft_rule *setup_rule(uint8_t family, const char *table,
> +				   const char *chain)
>  {
> -	struct nft_rule_expr *expr;
> +	struct nft_rule *r = NULL;
> +	uint8_t proto;
> +	uint16_t dport;
>  
> -	expr = nft_rule_expr_alloc("match");
> -	if (expr == NULL)
> -		return;
> +	r = nft_rule_alloc();
> +	if (r == NULL) {
> +		perror("OOM");
> +		exit(EXIT_FAILURE);
> +	}
>  
> -	add_match_iprange(expr);
> +	nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, table);
> +	nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, chain);
> +	nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family);
>  
> -	nft_rule_add_expr(r, expr);
> -}
> +	proto = IPPROTO_TCP;
> +	add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
> +		    offsetof(struct iphdr, protocol), sizeof(uint8_t));
> +	add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
>  
> -#define field_sizeof(t, f)	(sizeof(((t *)NULL)->f))
> +	dport = htons(22);
> +	add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
> +		    offsetof(struct tcphdr, dest), sizeof(uint16_t));
> +	add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
>  
> -static void add_payload2(struct nft_rule_expr *e)
> -{
> -	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE,
> -			      NFT_PAYLOAD_NETWORK_HEADER);
> -	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1);
> -	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET,
> -			      offsetof(struct iphdr, protocol));
> -	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, 1);
> +	add_counter(r);
> +
> +	return r;
>  }
>  
> -static void add_payload(struct nft_rule *r)
> +static int seq;
> +
> +static void nft_mnl_batch_put(struct mnl_nlmsg_batch *batch, int type)
>  {
> -	struct nft_rule_expr *expr;
> +	struct nlmsghdr *nlh;
> +	struct nfgenmsg *nfg;
>  
> -	expr = nft_rule_expr_alloc("payload");
> -	if (expr == NULL)
> -		return;
> +	nlh = mnl_nlmsg_put_header(mnl_nlmsg_batch_current(batch));
> +	nlh->nlmsg_type = type;
> +	nlh->nlmsg_flags = NLM_F_REQUEST;
> +	nlh->nlmsg_seq = seq++;
>  
> -	add_payload2(expr);
> +	nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
> +	nfg->nfgen_family = AF_INET;
> +	nfg->version = NFNETLINK_V0;
> +	nfg->res_id = NFNL_SUBSYS_NFTABLES;
>  
> -	nft_rule_add_expr(r, expr);
> +	mnl_nlmsg_batch_next(batch);
> +}
> +
> +static int nft_mnl_batch_talk(struct mnl_socket *nl, struct mnl_nlmsg_batch *b)
> +{
> +	int ret, fd = mnl_socket_get_fd(nl);
> +	char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
> +	fd_set readfds;
> +	struct timeval tv = {
> +		.tv_sec		= 0,
> +		.tv_usec	= 0
> +	};
> +
> +	ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b),
> +				mnl_nlmsg_batch_size(b));
> +	if (ret == -1)
> +		goto err;
> +
> +	FD_ZERO(&readfds);
> +	FD_SET(fd, &readfds);
> +
> +	/* receive and digest all the acknowledgments from the kernel. */
> +	ret = select(fd+1, &readfds, NULL, NULL, &tv);
> +	if (ret == -1)
> +		goto err;
> +
> +	while (ret > 0 && FD_ISSET(fd, &readfds)) {
> +		ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
> +		if (ret == -1)
> +			goto err;
> +
> +		ret = mnl_cb_run(rcv_buf, ret, 0, mnl_socket_get_portid(nl),
> +				 NULL, NULL);
> +		if (ret < 0)
> +			goto err;
> +
> +		ret = select(fd+1, &readfds, NULL, NULL, &tv);
> +		if (ret == -1)
> +			goto err;
> +
> +		FD_ZERO(&readfds);
> +		FD_SET(fd, &readfds);
> +	}
> +err:
> +	return ret;
>  }
>  
>  int main(int argc, char *argv[])
>  {
>  	struct mnl_socket *nl;
> -	char buf[MNL_SOCKET_BUFFER_SIZE];
> +	struct nft_rule *r;
>  	struct nlmsghdr *nlh;
> -	uint32_t portid, seq;
> -	struct nft_rule *r = NULL;
> -	int ret, family;
> +	struct mnl_nlmsg_batch *batch;
> +	uint8_t family;
> +	char buf[4096];
>  
>  	if (argc != 4) {
> -		fprintf(stderr, "Usage: %s <family> <table> <chain>\n",
> -			argv[0]);
> -		exit(EXIT_FAILURE);
> -	}
> -
> -	r = nft_rule_alloc();
> -	if (r == NULL) {
> -		perror("OOM");
> +		fprintf(stderr, "Usage: %s <family> <table> <chain>\n", argv[0]);
>  		exit(EXIT_FAILURE);
>  	}
>  
> @@ -141,32 +193,12 @@ int main(int argc, char *argv[])
>  		family = NFPROTO_IPV4;
>  	else if (strcmp(argv[1], "ip6") == 0)
>  		family = NFPROTO_IPV6;
> -	else if (strcmp(argv[1], "bridge") == 0)
> -		family = NFPROTO_BRIDGE;
> -	else if (strcmp(argv[1], "arp") == 0)
> -		family = NFPROTO_ARP;
>  	else {
> -		fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n");
> +		fprintf(stderr, "Unknown family: ip, ip6\n");
>  		exit(EXIT_FAILURE);
>  	}
>  
> -	nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, argv[2]);
> -	nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, argv[3]);
> -
> -	add_expr_match(r);
> -	add_payload(r);
> -	add_expr_target(r);
> -
> -	char tmp[1024];
> -	nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
> -	printf("%s\n", tmp);
> -
> -	seq = time(NULL);
> -	nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, family,
> -					NLM_F_APPEND|NLM_F_ACK|NLM_F_CREATE,
> -					seq);
> -	nft_rule_nlmsg_build_payload(nlh, r);
> -	nft_rule_free(r);
> +	r = setup_rule(family, argv[2], argv[3]);
>  
>  	nl = mnl_socket_open(NETLINK_NETFILTER);
>  	if (nl == NULL) {
> @@ -178,24 +210,29 @@ int main(int argc, char *argv[])
>  		perror("mnl_socket_bind");
>  		exit(EXIT_FAILURE);
>  	}
> -	portid = mnl_socket_get_portid(nl);
>  
> -	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
> -		perror("mnl_socket_send");
> -		exit(EXIT_FAILURE);
> -	}
> +	batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
>  
> -	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
> -	while (ret > 0) {
> -		ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
> -		if (ret <= 0)
> -			break;
> -		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
> -	}
> -	if (ret == -1) {
> -		perror("error");
> +	nft_mnl_batch_put(batch, NFNL_MSG_BATCH_BEGIN);
> +
> +	nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
> +			NFT_MSG_NEWRULE,
> +			nft_rule_attr_get_u32(r, NFT_RULE_ATTR_FAMILY),
> +			NLM_F_APPEND|NLM_F_CREATE, seq);
> +
> +	nft_rule_nlmsg_build_payload(nlh, r);
> +	nft_rule_free(r);
> +	mnl_nlmsg_batch_next(batch);
> +
> +	nft_mnl_batch_put(batch, NFNL_MSG_BATCH_END);
> +
> +	if (nft_mnl_batch_talk(nl, batch) < 0) {
> +		perror("Netlink problem");
>  		exit(EXIT_FAILURE);
>  	}
> +
> +	mnl_nlmsg_batch_stop(batch);
> +
>  	mnl_socket_close(nl);
>  
>  	return EXIT_SUCCESS;
> diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
> index 91eebab..336c10c 100644
> --- a/include/linux/netfilter/nfnetlink.h
> +++ b/include/linux/netfilter/nfnetlink.h
> @@ -97,4 +97,9 @@ extern void nfnl_unlock(void);
>  	MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
>  
>  #endif	/* __KERNEL__ */
> +
> +/* Reserved control nfnetlink messages */
> +#define NFNL_MSG_BATCH_BEGIN            NLMSG_MIN_TYPE
> +#define NFNL_MSG_BATCH_END              NLMSG_MIN_TYPE+1
> +
>  #endif	/* _NFNETLINK_H */
Eric Leblond Jan. 2, 2014, 12:26 a.m. UTC | #2
Hello,

On Thu, 2014-01-02 at 01:06 +0100, Arturo Borrero Gonzalez wrote:
> 
> El 01/01/2014 23:39, "Eric Leblond" <eric@regit.org> escribió:
> >
> > I don't see this patch in any of the branch. Is there any issue with
> > it ?
> >
> > It seems really useful as existing examples are using really old
> code.
> >
> 
> I think you are looking for
> http://git.netfilter.org/libnftables/commit/examples/nft-rule-add.c?id=0b3161731262d3a8c6110c17fd818af325dbf491

Indeed! Sorry for the noise.

New year resolution: double check my git commands before mailing ;)

BR,
diff mbox

Patch

diff --git a/examples/nft-rule-add.c b/examples/nft-rule-add.c
index f896bc0..0534fa5 100644
--- a/examples/nft-rule-add.c
+++ b/examples/nft-rule-add.c
@@ -14,126 +14,178 @@ 
 #include <string.h>
 #include <stddef.h>	/* for offsetof */
 #include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
 
 #include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
 
 #include <libmnl/libmnl.h>
 #include <libnftables/rule.h>
 #include <libnftables/expr.h>
 
-#include <linux/netfilter_ipv4/ipt_LOG.h>
-#include <linux/netfilter/xt_iprange.h>
-
-#include <netinet/ip.h>
-
-static void add_target_log(struct nft_rule_expr *e)
+static void add_payload(struct nft_rule *r, uint32_t base, uint32_t dreg,
+			uint32_t offset, uint32_t len)
 {
-	struct ipt_log_info *info;
+	struct nft_rule_expr *e;
 
-	nft_rule_expr_set(e, NFT_EXPR_TG_NAME, "LOG", strlen("LOG"));
-	nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, 0);
-
-	info = calloc(1, sizeof(struct ipt_log_info));
-	if (info == NULL)
-		return;
+	e = nft_rule_expr_alloc("payload");
+	if (e == NULL) {
+		perror("expr payload oom");
+		exit(EXIT_FAILURE);
+	}
 
-	sprintf(info->prefix, "test: ");
-	info->prefix[sizeof(info->prefix)-1] = '\0';
-	info->logflags = 0x0f;
-	info->level = 5;
+	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base);
+	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, dreg);
+	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, offset);
+	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, len);
 
-	nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, sizeof(*info));
+	nft_rule_add_expr(r, e);
 }
 
-static void add_expr_target(struct nft_rule *r)
+static void add_cmp(struct nft_rule *r, uint32_t sreg, uint32_t op,
+		    const void *data, uint32_t data_len)
 {
-	struct nft_rule_expr *expr;
+	struct nft_rule_expr *e;
 
-	expr = nft_rule_expr_alloc("target");
-	if (expr == NULL)
-		return;
+	e = nft_rule_expr_alloc("cmp");
+	if (e == NULL) {
+		perror("expr cmp oom");
+		exit(EXIT_FAILURE);
+	}
 
-	add_target_log(expr);
+	nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, sreg);
+	nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, op);
+	nft_rule_expr_set(e, NFT_EXPR_CMP_DATA, data, data_len);
 
-	nft_rule_add_expr(r, expr);
+	nft_rule_add_expr(r, e);
 }
 
-static void add_match_iprange(struct nft_rule_expr *e)
+static void add_counter(struct nft_rule *r)
 {
-	struct xt_iprange_mtinfo *info;
-
-	nft_rule_expr_set(e, NFT_EXPR_MT_NAME, "iprange", strlen("iprange"));
-	nft_rule_expr_set_u32(e, NFT_EXPR_MT_REV, 1);
+	struct nft_rule_expr *e;
 
-	info = calloc(1, sizeof(struct xt_iprange_mtinfo));
-	if (info == NULL)
-		return;
-
-	info->src_min.ip = info->dst_min.ip = inet_addr("127.0.0.1");
-	info->src_max.ip = info->dst_max.ip = inet_addr("127.0.0.1");
-	info->flags = IPRANGE_SRC;
+	e = nft_rule_expr_alloc("counter");
+	if (e == NULL) {
+		perror("expr counter oom");
+		exit(EXIT_FAILURE);
+	}
 
-	nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, sizeof(*info));
+	nft_rule_add_expr(r, e);
 }
 
-static void add_expr_match(struct nft_rule *r)
+static struct nft_rule *setup_rule(uint8_t family, const char *table,
+				   const char *chain)
 {
-	struct nft_rule_expr *expr;
+	struct nft_rule *r = NULL;
+	uint8_t proto;
+	uint16_t dport;
 
-	expr = nft_rule_expr_alloc("match");
-	if (expr == NULL)
-		return;
+	r = nft_rule_alloc();
+	if (r == NULL) {
+		perror("OOM");
+		exit(EXIT_FAILURE);
+	}
 
-	add_match_iprange(expr);
+	nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, table);
+	nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, chain);
+	nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, family);
 
-	nft_rule_add_expr(r, expr);
-}
+	proto = IPPROTO_TCP;
+	add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
+		    offsetof(struct iphdr, protocol), sizeof(uint8_t));
+	add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));
 
-#define field_sizeof(t, f)	(sizeof(((t *)NULL)->f))
+	dport = htons(22);
+	add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
+		    offsetof(struct tcphdr, dest), sizeof(uint16_t));
+	add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));
 
-static void add_payload2(struct nft_rule_expr *e)
-{
-	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE,
-			      NFT_PAYLOAD_NETWORK_HEADER);
-	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1);
-	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET,
-			      offsetof(struct iphdr, protocol));
-	nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, 1);
+	add_counter(r);
+
+	return r;
 }
 
-static void add_payload(struct nft_rule *r)
+static int seq;
+
+static void nft_mnl_batch_put(struct mnl_nlmsg_batch *batch, int type)
 {
-	struct nft_rule_expr *expr;
+	struct nlmsghdr *nlh;
+	struct nfgenmsg *nfg;
 
-	expr = nft_rule_expr_alloc("payload");
-	if (expr == NULL)
-		return;
+	nlh = mnl_nlmsg_put_header(mnl_nlmsg_batch_current(batch));
+	nlh->nlmsg_type = type;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+	nlh->nlmsg_seq = seq++;
 
-	add_payload2(expr);
+	nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_INET;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = NFNL_SUBSYS_NFTABLES;
 
-	nft_rule_add_expr(r, expr);
+	mnl_nlmsg_batch_next(batch);
+}
+
+static int nft_mnl_batch_talk(struct mnl_socket *nl, struct mnl_nlmsg_batch *b)
+{
+	int ret, fd = mnl_socket_get_fd(nl);
+	char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
+	fd_set readfds;
+	struct timeval tv = {
+		.tv_sec		= 0,
+		.tv_usec	= 0
+	};
+
+	ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b),
+				mnl_nlmsg_batch_size(b));
+	if (ret == -1)
+		goto err;
+
+	FD_ZERO(&readfds);
+	FD_SET(fd, &readfds);
+
+	/* receive and digest all the acknowledgments from the kernel. */
+	ret = select(fd+1, &readfds, NULL, NULL, &tv);
+	if (ret == -1)
+		goto err;
+
+	while (ret > 0 && FD_ISSET(fd, &readfds)) {
+		ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
+		if (ret == -1)
+			goto err;
+
+		ret = mnl_cb_run(rcv_buf, ret, 0, mnl_socket_get_portid(nl),
+				 NULL, NULL);
+		if (ret < 0)
+			goto err;
+
+		ret = select(fd+1, &readfds, NULL, NULL, &tv);
+		if (ret == -1)
+			goto err;
+
+		FD_ZERO(&readfds);
+		FD_SET(fd, &readfds);
+	}
+err:
+	return ret;
 }
 
 int main(int argc, char *argv[])
 {
 	struct mnl_socket *nl;
-	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nft_rule *r;
 	struct nlmsghdr *nlh;
-	uint32_t portid, seq;
-	struct nft_rule *r = NULL;
-	int ret, family;
+	struct mnl_nlmsg_batch *batch;
+	uint8_t family;
+	char buf[4096];
 
 	if (argc != 4) {
-		fprintf(stderr, "Usage: %s <family> <table> <chain>\n",
-			argv[0]);
-		exit(EXIT_FAILURE);
-	}
-
-	r = nft_rule_alloc();
-	if (r == NULL) {
-		perror("OOM");
+		fprintf(stderr, "Usage: %s <family> <table> <chain>\n", argv[0]);
 		exit(EXIT_FAILURE);
 	}
 
@@ -141,32 +193,12 @@  int main(int argc, char *argv[])
 		family = NFPROTO_IPV4;
 	else if (strcmp(argv[1], "ip6") == 0)
 		family = NFPROTO_IPV6;
-	else if (strcmp(argv[1], "bridge") == 0)
-		family = NFPROTO_BRIDGE;
-	else if (strcmp(argv[1], "arp") == 0)
-		family = NFPROTO_ARP;
 	else {
-		fprintf(stderr, "Unknown family: ip, ip6, bridge, arp\n");
+		fprintf(stderr, "Unknown family: ip, ip6\n");
 		exit(EXIT_FAILURE);
 	}
 
-	nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, argv[2]);
-	nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, argv[3]);
-
-	add_expr_match(r);
-	add_payload(r);
-	add_expr_target(r);
-
-	char tmp[1024];
-	nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
-	printf("%s\n", tmp);
-
-	seq = time(NULL);
-	nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, family,
-					NLM_F_APPEND|NLM_F_ACK|NLM_F_CREATE,
-					seq);
-	nft_rule_nlmsg_build_payload(nlh, r);
-	nft_rule_free(r);
+	r = setup_rule(family, argv[2], argv[3]);
 
 	nl = mnl_socket_open(NETLINK_NETFILTER);
 	if (nl == NULL) {
@@ -178,24 +210,29 @@  int main(int argc, char *argv[])
 		perror("mnl_socket_bind");
 		exit(EXIT_FAILURE);
 	}
-	portid = mnl_socket_get_portid(nl);
 
-	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-		perror("mnl_socket_send");
-		exit(EXIT_FAILURE);
-	}
+	batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
 
-	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
-	while (ret > 0) {
-		ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
-		if (ret <= 0)
-			break;
-		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
-	}
-	if (ret == -1) {
-		perror("error");
+	nft_mnl_batch_put(batch, NFNL_MSG_BATCH_BEGIN);
+
+	nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+			NFT_MSG_NEWRULE,
+			nft_rule_attr_get_u32(r, NFT_RULE_ATTR_FAMILY),
+			NLM_F_APPEND|NLM_F_CREATE, seq);
+
+	nft_rule_nlmsg_build_payload(nlh, r);
+	nft_rule_free(r);
+	mnl_nlmsg_batch_next(batch);
+
+	nft_mnl_batch_put(batch, NFNL_MSG_BATCH_END);
+
+	if (nft_mnl_batch_talk(nl, batch) < 0) {
+		perror("Netlink problem");
 		exit(EXIT_FAILURE);
 	}
+
+	mnl_nlmsg_batch_stop(batch);
+
 	mnl_socket_close(nl);
 
 	return EXIT_SUCCESS;
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 91eebab..336c10c 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -97,4 +97,9 @@  extern void nfnl_unlock(void);
 	MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
 
 #endif	/* __KERNEL__ */
+
+/* Reserved control nfnetlink messages */
+#define NFNL_MSG_BATCH_BEGIN            NLMSG_MIN_TYPE
+#define NFNL_MSG_BATCH_END              NLMSG_MIN_TYPE+1
+
 #endif	/* _NFNETLINK_H */