summaryrefslogtreecommitdiffstats
path: root/src/mnl.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2014-05-19 12:18:04 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2014-05-19 12:18:06 +0200
commit337636fc670eaed5c7146f99af8719018f63fe3b (patch)
tree7ddacf7ef5052951706748180432e6c0605319b7 /src/mnl.c
parentef184c4e844eac8c3eda0c8c6fc1370078a9d66a (diff)
src: add set netlink message to the batch
This patch moves the netlink set messages to the batch that contains the rules. This helps to speed up rule-set restoration time by changing the operational. To achieve this, an internal set ID which is unique to the batch is allocated as suggested by Patrick. To retain backward compatibility, nft initially guesses if the kernel supports set in batches. Otherwise, it falls back to the previous (slowier) operational. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/mnl.c')
-rw-r--r--src/mnl.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/src/mnl.c b/src/mnl.c
index 4d60d64b..873d442c 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -630,6 +630,38 @@ int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
return nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
}
+int mnl_nft_set_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags, uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_NEWSET,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_CREATE | flags, seqnum);
+ nft_set_nlmsg_build_payload(nlh, nls);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_batch_page_add();
+
+ return 0;
+}
+
+int mnl_nft_set_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags, uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_DELSET,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ flags, seqnum);
+ nft_set_nlmsg_build_payload(nlh, nls);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_batch_page_add();
+
+ return 0;
+}
+
static int set_cb(const struct nlmsghdr *nlh, void *data)
{
struct nft_set_list *nls_list = data;
@@ -742,6 +774,38 @@ static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
+int mnl_nft_setelem_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags, uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_elem_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_NEWSETELEM,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_CREATE | flags, seqnum);
+ nft_set_elems_nlmsg_build_payload(nlh, nls);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_batch_page_add();
+
+ return 0;
+}
+
+int mnl_nft_setelem_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags, uint32_t seqnum)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_elem_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ NFT_MSG_DELSETELEM,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ 0, seqnum);
+ nft_set_elems_nlmsg_build_payload(nlh, nls);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_batch_page_add();
+
+ return 0;
+}
+
int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
@@ -823,3 +887,68 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock,
{
return nft_mnl_recv(nf_sock, 0, 0, cb, cb_data);
}
+
+static void nft_mnl_batch_put(char *buf, uint16_t type, uint32_t seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nlh->nlmsg_seq = seq;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = NFNL_SUBSYS_NFTABLES;
+}
+
+bool mnl_batch_supported(struct mnl_socket *nf_sock)
+{
+ struct mnl_nlmsg_batch *b;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ b = mnl_nlmsg_batch_start(buf, sizeof(buf));
+
+ nft_mnl_batch_put(mnl_nlmsg_batch_current(b), NFNL_MSG_BATCH_BEGIN,
+ seq++);
+ mnl_nlmsg_batch_next(b);
+
+ nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(b),
+ NFT_MSG_NEWSET, AF_INET,
+ NLM_F_ACK, seq++);
+ mnl_nlmsg_batch_next(b);
+
+ nft_mnl_batch_put(mnl_nlmsg_batch_current(b), NFNL_MSG_BATCH_END,
+ seq++);
+ mnl_nlmsg_batch_next(b);
+
+ ret = mnl_socket_sendto(nf_sock, mnl_nlmsg_batch_head(b),
+ mnl_nlmsg_batch_size(b));
+ if (ret < 0)
+ goto err;
+
+ mnl_nlmsg_batch_stop(b);
+
+ ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nf_sock),
+ NULL, NULL);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+ }
+
+ /* We're sending an incomplete message to see if the kernel supports
+ * set messages in batches. EINVAL means that we sent an incomplete
+ * message with missing attributes. The kernel just ignores messages
+ * that we cannot include in the batch.
+ */
+ return (ret == -1 && errno == EINVAL) ? true : false;
+err:
+ mnl_nlmsg_batch_stop(b);
+ return ret;
+}