diff --git a/src/mnl.c b/src/mnl.c index 26f943db..dcc22b82 100644 --- a/src/mnl.c +++ b/src/mnl.c @@ -259,10 +259,19 @@ static void mnl_set_sndbuffer(const struct mnl_socket *nl, if (newbuffsiz <= sndnlbuffsiz) return; - /* Rise sender buffer length to avoid hitting -EMSGSIZE */ - if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE, - &newbuffsiz, sizeof(socklen_t)) < 0) - return; + /* Raise sender buffer length to avoid hitting -EMSGSIZE. The kernel may + * reduce this to /proc/sys/net/core/wmem_max, see socket(7). + */ + sndnlbuffsiz = newbuffsiz; + if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF, + &sndnlbuffsiz, sizeof(socklen_t)) < 0 || sndnlbuffsiz < newbuffsiz) { + /* If SO_SNDBUF failed or the resulting size is still too small, try + * again with SO_SNDBUFFORCE. This requires CAP_NET_ADMIN. + */ + sndnlbuffsiz = newbuffsiz; + setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE, + &sndnlbuffsiz, sizeof(socklen_t)); + } } static unsigned int nlsndbufsiz; @@ -280,14 +289,16 @@ static int mnl_set_rcvbuffer(const struct mnl_socket *nl, socklen_t bufsiz) if (nlsndbufsiz >= bufsiz) return 0; - ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, - &bufsiz, sizeof(socklen_t)); - if (ret < 0) { - /* If this doesn't work, try to reach the system wide maximum - * (or whatever the user requested). + nlsndbufsiz = bufsiz; + ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUF, + &nlsndbufsiz, sizeof(socklen_t)); + if (ret < 0 || nlsndbufsiz < bufsiz) { + /* If this doesn't work, try again with SO_RCVBUFFORCE. This requires + * CAP_NET_ADMIN. */ - ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUF, - &bufsiz, sizeof(socklen_t)); + nlsndbufsiz = bufsiz; + ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, + &nlsndbufsiz, sizeof(socklen_t)); } return ret;