mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 12:13:33 +08:00
netlink: port to recvmsg_safe()
This also makes sure the control buffer is properly aligned. This matters, as otherwise the control buffer might not be aligned and the cmsg buffer counting might be off. The incorrect alignment is becoming visible by using recvmsg_safe() as we suddenly notice the MSG_CTRUNC bit set because of this. That said, apparently this isn't enough to make this work on all kernels. Since I couldn't figure this out, we now add 1K to the buffer to be sure. We do this once already, also for a pktinfo structure (though an IPv4/IPv6) one. I am puzzled by this, but this shouldn't matter much. it works locally just fine, except for those ubuntu CI kernels... While we are at it, make some other changes too, to simplify and modernize the function.
This commit is contained in:
parent
d423294394
commit
9e45fb09bf
@ -16,6 +16,10 @@
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* For some reason we need some extra cmsg space on some kernels. It's not clear why, and one of those days
|
||||
* we need to track this down. See: https://github.com/systemd/systemd/pull/15457 */
|
||||
#define EXTRA_CMSG_SPACE 1024
|
||||
|
||||
int socket_open(int family) {
|
||||
int fd;
|
||||
|
||||
@ -240,30 +244,27 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
|
||||
|
||||
static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) {
|
||||
union sockaddr_union sender;
|
||||
uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo)) + EXTRA_CMSG_SPACE) control;
|
||||
struct msghdr msg = {
|
||||
.msg_iov = iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_name = &sender,
|
||||
.msg_namelen = sizeof(sender),
|
||||
.msg_control = cmsg_buffer,
|
||||
.msg_controllen = sizeof(cmsg_buffer),
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
ssize_t n;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(iov);
|
||||
|
||||
n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
|
||||
if (n < 0) {
|
||||
/* no data */
|
||||
if (errno == ENOBUFS)
|
||||
log_debug("rtnl: kernel receive buffer overrun");
|
||||
else if (errno == EAGAIN)
|
||||
log_debug("rtnl: no data in socket");
|
||||
|
||||
return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
|
||||
}
|
||||
n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
|
||||
if (n == -ENOBUFS)
|
||||
return log_debug_errno(n, "rtnl: kernel receive buffer overrun");
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
return 0;
|
||||
if (n < 0)
|
||||
return (int) n;
|
||||
|
||||
if (sender.nl.nl_pid != 0) {
|
||||
/* not from the kernel, ignore */
|
||||
@ -271,20 +272,20 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_gr
|
||||
|
||||
if (peek) {
|
||||
/* drop the message */
|
||||
n = recvmsg(fd, &msg, 0);
|
||||
n = recvmsg_safe(fd, &msg, 0);
|
||||
if (n < 0)
|
||||
return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret_mcast_group) {
|
||||
struct cmsghdr *cmsg;
|
||||
struct nl_pktinfo *pi;
|
||||
|
||||
cmsg = cmsg_find(&msg, SOL_NETLINK, NETLINK_PKTINFO, CMSG_LEN(sizeof(struct nl_pktinfo)));
|
||||
if (cmsg)
|
||||
*ret_mcast_group = ((struct nl_pktinfo*) CMSG_DATA(cmsg))->group;
|
||||
pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo);
|
||||
if (pi)
|
||||
*ret_mcast_group = pi->group;
|
||||
else
|
||||
*ret_mcast_group = 0;
|
||||
}
|
||||
|
@ -165,6 +165,7 @@ void manager_verify_all(Manager *m);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we ned to figure out why */
|
||||
#define EXTRA_CMSG_SPACE 1024
|
||||
|
||||
int manager_is_own_hostname(Manager *m, const char *name);
|
||||
|
Loading…
Reference in New Issue
Block a user