]> 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, 24 Apr 2023 20:48:42 +0000 (22:48 +0200)
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 0d97f71ccff37729188a98dce4d0f41e1419f621..d52434c72bc2c9d765e3d20112e0b63f227f767d 100644 (file)
@@ -84,6 +84,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 4f538c44b998a6868a1c62630751c15359b2f6da..de16d203a0170b2a4e8c663ded1fd1e9505f8d1c 100644 (file)
@@ -55,6 +55,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 26f943dbb4c8e09c764755830747a97d271bbed7..ce9e4ee1c0598e4c1560cee1d664cd5ffd92b551 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -245,9 +245,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;
@@ -260,9 +261,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;
@@ -409,7 +416,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);
+}