]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
mnl: estimate receiver buffer size
authorPablo Neira Ayuso <pablo@netfilter.org>
Wed, 29 May 2019 18:23:23 +0000 (20:23 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 31 May 2019 15:57:35 +0000 (17:57 +0200)
Set a receiver buffer size based on the number of commands and the
average message size, this is useful for the --echo option in order to
avoid ENOBUFS errors.

On the kernel side, each skbuff consumes truesize from the socket queue
(although it uses NLMSG_GOODSIZE to allocate it), which is approximately
four times the estimated size per message that we get in turn for each
echo message to ensure enough receiver buffer space.

We could also explore increasing the buffer and retry if
mnl_nft_socket_sendmsg() hits ENOBUFS if we ever hit this problem again.

Reported-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/mnl.h
src/libnftables.c
src/mnl.c

index c63a7e7fd73aba62f06b7bf39c6b420718d89b38..9f50c3da0f3a626480be9385692eac6d6726cb94 100644 (file)
@@ -25,7 +25,8 @@ bool mnl_batch_ready(struct nftnl_batch *batch);
 void mnl_batch_reset(struct nftnl_batch *batch);
 uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t seqnum);
 void mnl_batch_end(struct nftnl_batch *batch, uint32_t seqnum);
-int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list);
+int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
+                  uint32_t num_cmds);
 
 int mnl_nft_rule_add(struct netlink_ctx *ctx, const struct cmd *cmd,
                     unsigned int flags);
index 199dbc97b801c8171800485cf45a05b32a46d9e7..a58b8ca9dcf6445d1be6a0ece5754ff8cf390b2e 100644 (file)
@@ -21,7 +21,7 @@ static int nft_netlink(struct nft_ctx *nft,
                       struct list_head *cmds, struct list_head *msgs,
                       struct mnl_socket *nf_sock)
 {
-       uint32_t batch_seqnum, seqnum = 0;
+       uint32_t batch_seqnum, seqnum = 0, num_cmds = 0;
        struct nftnl_batch *batch;
        struct netlink_ctx ctx;
        struct cmd *cmd;
@@ -49,6 +49,7 @@ static int nft_netlink(struct nft_ctx *nft,
                                         strerror(errno));
                        goto out;
                }
+               num_cmds++;
        }
        if (!nft->check)
                mnl_batch_end(batch, mnl_seqnum_alloc(&seqnum));
@@ -56,7 +57,7 @@ static int nft_netlink(struct nft_ctx *nft,
        if (!mnl_batch_ready(batch))
                goto out;
 
-       ret = mnl_batch_talk(&ctx, &err_list);
+       ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
 
        list_for_each_entry_safe(err, tmp, &err_list, head) {
                list_for_each_entry(cmd, cmds, list) {
index e70f8b3986b0f4f4e7030326104801851dac61d8..4f6f43d23916e808970cbb915532ef50767230c8 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -299,12 +299,14 @@ static ssize_t mnl_nft_socket_sendmsg(struct netlink_ctx *ctx,
        return sendmsg(mnl_socket_get_fd(ctx->nft->nf_sock), msg, 0);
 }
 
-int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list)
+int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
+                  uint32_t num_cmds)
 {
        struct mnl_socket *nl = ctx->nft->nf_sock;
        int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl);
        uint32_t iov_len = nftnl_batch_iovec_len(ctx->batch);
        char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
+       size_t avg_msg_size, batch_size;
        const struct sockaddr_nl snl = {
                .nl_family = AF_NETLINK
        };
@@ -319,7 +321,10 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list)
 
        mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
 
-       mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+       batch_size = mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+       avg_msg_size = div_round_up(batch_size, num_cmds);
+
+       mnl_set_rcvbuffer(ctx->nft->nf_sock, num_cmds * avg_msg_size * 4);
 
        ret = mnl_nft_socket_sendmsg(ctx, &msg);
        if (ret == -1)