]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
mnl: set SO_SNDBUF before SO_SNDBUFFORCE
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 7 Apr 2023 22:21:57 +0000 (16:21 -0600)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 1 Sep 2025 20:34:44 +0000 (22:34 +0200)
commit 375505a4a8068bf7cb623e18c3aedb831c17fd0e upstream.

Set SO_SNDBUF before SO_SNDBUFFORCE: Unpriviledged user namespace does
not have CAP_NET_ADMIN on the host (user_init_ns) namespace.

SO_SNDBUF always succeeds in Linux, always try SO_SNDBUFFORCE after it.

Moreover, suggest the user to bump socket limits if EMSGSIZE after
having see EPERM previously, when calling SO_SNDBUFFORCE.

Provide a hint to the user too:

 # nft -f test.nft
 netlink: Error: Could not process rule: Message too long
 Please, rise /proc/sys/net/core/wmem_max on the host namespace. Hint: 4194304 bytes

Dave Pfike says:

 Prior to this patch, nft inside a systemd-nspawn container was failing
 to install my ruleset (which includes a large-ish map), with the error

 netlink: Error: Could not process rule: Message too long

 strace reveals:

 setsockopt(3, SOL_SOCKET, SO_SNDBUFFORCE, [524288], 4) = -1 EPERM (Operation not permitted)

 This is despite the nspawn process supposedly having CAP_NET_ADMIN.

 A web search reveals at least one other user having the same issue:

 https://old.reddit.com/r/Proxmox/comments/scnoav/lxc_container_debian_11_nftables_geoblocking/

Reported-by: Dave Pifke <dave@pifke.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/netlink.h
include/utils.h
src/libnftables.c
src/mnl.c
src/utils.c

index e7d36ac6fe287c4d6411198454c581ad63799f81..becfdce66eb74703646d1ee149ccb608a2a9569b 100644 (file)
@@ -78,6 +78,7 @@ struct netlink_ctx {
        const void              *data;
        uint32_t                seqnum;
        struct nftnl_batch      *batch;
+       int                     maybe_emsgsize;
 };
 
 extern struct nftnl_expr *alloc_nft_expr(const char *name);
index ffbe2cbb75be57c64fb3d59233ab8efaebb285d3..d5073e061033c642acf98f25db5236e2fde91fb6 100644 (file)
@@ -136,5 +136,6 @@ extern void *xzalloc(size_t size);
 extern void *xzalloc_array(size_t nmemb, size_t size);
 extern char *xstrdup(const char *s);
 extern void xstrunescape(const char *in, char *out);
+extern int round_pow_2(unsigned int value);
 
 #endif /* NFTABLES_UTILS_H */
index fdd18fc99b32fda1c7c8871435e5d273554c99c1..f1841c7e700967b41334aa74e7bd4829e3729f0c 100644 (file)
@@ -56,6 +56,13 @@ static int nft_netlink(struct nft_ctx *nft,
 
        ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
        if (ret < 0) {
+               if (ctx.maybe_emsgsize && errno == EMSGSIZE) {
+                       netlink_io_error(&ctx, NULL,
+                                        "Could not process rule: %s\n"
+                                        "Please, rise /proc/sys/net/core/wmem_max on the host namespace. Hint: %d bytes",
+                                        strerror(errno), round_pow_2(ctx.maybe_emsgsize));
+                       goto out;
+               }
                netlink_io_error(&ctx, NULL,
                                 "Could not process rule: %s", strerror(errno));
                goto out;
index 2d44a1ca692a68b60ad6d2d8fa530489c7464550..b84b59aa0fdbcce2945916bb1e07aead0508b4a5 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -244,9 +244,10 @@ void mnl_err_list_free(struct mnl_err *err)
        xfree(err);
 }
 
-static void mnl_set_sndbuffer(const struct mnl_socket *nl,
-                             struct nftnl_batch *batch)
+static void mnl_set_sndbuffer(struct netlink_ctx *ctx)
 {
+       struct mnl_socket *nl = ctx->nft->nf_sock;
+       struct nftnl_batch *batch = ctx->batch;
        socklen_t len = sizeof(int);
        int sndnlbuffsiz = 0;
        int newbuffsiz;
@@ -259,9 +260,15 @@ static void mnl_set_sndbuffer(const struct mnl_socket *nl,
                return;
 
        /* Rise sender buffer length to avoid hitting -EMSGSIZE */
+       setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF,
+                  &newbuffsiz, sizeof(socklen_t));
+
+       /* unpriviledged containers check for CAP_NET_ADMIN on the init_user_ns. */
        if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
-                      &newbuffsiz, sizeof(socklen_t)) < 0)
-               return;
+                      &newbuffsiz, sizeof(socklen_t)) < 0) {
+               if (errno == EPERM)
+                       ctx->maybe_emsgsize = newbuffsiz;
+       }
 }
 
 static unsigned int nlsndbufsiz;
@@ -408,7 +415,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
                .nl_ctx = ctx,
        };
 
-       mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
+       mnl_set_sndbuffer(ctx);
 
        mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
 
index 925841c571f5d8536b24f8bf84866fd142ddf317..a5815018c775c17c055aee40fbf50d1ace9e0b81 100644 (file)
@@ -100,3 +100,8 @@ void xstrunescape(const char *in, char *out)
        }
        out[k++] = '\0';
 }
+
+int round_pow_2(unsigned int n)
+{
+       return 1UL << fls(n - 1);
+}