diff options
Diffstat (limited to 'src/libsystemd')
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-internal.h | 3 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-message.c | 57 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/sd-rtnl.c | 8 |
3 files changed, 38 insertions, 30 deletions
diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h index 2f788d04f6..6dd65b2de7 100644 --- a/src/libsystemd/sd-rtnl/rtnl-internal.h +++ b/src/libsystemd/sd-rtnl/rtnl-internal.h @@ -72,6 +72,9 @@ struct sd_rtnl { unsigned wqueue_size; size_t wqueue_allocated; + struct nlmsghdr *rbuffer; + size_t rbuffer_allocated; + bool processing:1; uint32_t serial; diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 3a24cb885f..a93cb0c8e4 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -997,26 +997,6 @@ int sd_rtnl_message_get_errno(sd_rtnl_message *m) { return err->error; } -static int message_receive_need(sd_rtnl *rtnl, size_t *need) { - assert(rtnl); - assert(need); - - /* ioctl(rtnl->fd, FIONREAD, &need) - Does not appear to work on netlink sockets. libnl uses - MSG_PEEK instead. I don't know if that is worth the - extra roundtrip. - - For now we simply use the maximum message size the kernel - may use (NLMSG_GOODSIZE), and then realloc to the actual - size after reading the message (hence avoiding huge memory - usage in case many small messages are kept around) */ - *need = page_size(); - if (*need > 8192UL) - *need = 8192UL; - - return 0; -} - int rtnl_message_parse(sd_rtnl_message *m, size_t **rta_offset_tb, unsigned short *rta_tb_size, @@ -1082,7 +1062,6 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { int socket_read_message(sd_rtnl *rtnl) { _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL; sd_rtnl_message *previous = NULL; - _cleanup_free_ void *buffer = NULL; uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))]; struct iovec iov = {}; struct msghdr msg = { @@ -1094,23 +1073,37 @@ int socket_read_message(sd_rtnl *rtnl) { struct cmsghdr *cmsg; bool auth = false; struct nlmsghdr *new_msg; - size_t need, len; + size_t len; int r, ret = 0; assert(rtnl); + assert(rtnl->rbuffer); - r = message_receive_need(rtnl, &need); + iov.iov_base = rtnl->rbuffer; + iov.iov_len = rtnl->rbuffer_allocated; + + /* peek at the pending message header to get the message size */ + r = recvmsg(rtnl->fd, &msg, MSG_PEEK); if (r < 0) - return r; + /* no data */ + return (errno == EAGAIN) ? 0 : -errno; + else if (r == 0) + /* connection was closed by the kernel */ + return -ECONNRESET; + else if ((size_t)r < sizeof(struct nlmsghdr)) + return -EIO; - buffer = malloc0(need); - if (!buffer) + /* make room for the pending message */ + if (!greedy_realloc((void **)&rtnl->rbuffer, + &rtnl->rbuffer_allocated, + rtnl->rbuffer->nlmsg_len, + sizeof(uint8_t))) return -ENOMEM; - iov.iov_base = buffer; - iov.iov_len = need; + iov.iov_base = rtnl->rbuffer; + iov.iov_len = rtnl->rbuffer_allocated; - r = recvmsg(rtnl->fd, &msg, 0); + r = recvmsg(rtnl->fd, &msg, MSG_TRUNC); if (r < 0) /* no data */ return (errno == EAGAIN) ? 0 : -errno; @@ -1120,6 +1113,10 @@ int socket_read_message(sd_rtnl *rtnl) { else len = (size_t)r; + if (len > rtnl->rbuffer_allocated) + /* message did not fit in read buffer */ + return -EIO; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS && @@ -1138,7 +1135,7 @@ int socket_read_message(sd_rtnl *rtnl) { /* not from the kernel, ignore */ return 0; - for (new_msg = buffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) { + for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; const NLType *nl_type; diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index 367f165a1c..e4d436bb17 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -54,6 +54,12 @@ static int sd_rtnl_new(sd_rtnl **ret) { if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1)) return -ENOMEM; + /* We guarantee that the read buffer has at least space for + * a message header */ + if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated, + sizeof(struct nlmsghdr), sizeof(uint8_t))) + return -ENOMEM; + *ret = rtnl; rtnl = NULL; @@ -133,6 +139,8 @@ sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) { sd_rtnl_message_unref(rtnl->wqueue[i]); free(rtnl->wqueue); + free(rtnl->rbuffer); + hashmap_free_free(rtnl->reply_callbacks); prioq_free(rtnl->reply_callbacks_prioq); |