int r;
assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL);
- assert_return(index > 0, -EINVAL);
- assert_return(family == AF_INET || family == AF_INET6, -EINVAL);
+ assert_return((nlmsg_type == RTM_GETADDR && index == 0) ||
+ index > 0, -EINVAL);
+ assert_return((nlmsg_type == RTM_GETADDR && family == AF_UNSPEC) ||
+ family == AF_INET || family == AF_INET6, -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
if (r < 0)
return r;
- if (nlmsg_type == RTM_GETADDR && family == AF_INET)
+ if (nlmsg_type == RTM_GETADDR)
(*ret)->hdr->nlmsg_flags |= NLM_F_DUMP;
ifa = NLMSG_DATA((*ret)->hdr);
for (i = 0; i <= m->n_containers; i++)
free(m->rta_offset_tb[i]);
+ sd_rtnl_message_unref(m->next);
+
free(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,
* On failure, a negative error code is returned.
*/
int socket_read_message(sd_rtnl *rtnl) {
- _cleanup_free_ void *buffer = NULL;
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL;
+ sd_rtnl_message *previous = NULL;
uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
struct iovec iov = {};
struct msghdr msg = {
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;
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 &&
/* 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;
if (new_msg->nlmsg_type == NLMSG_NOOP)
continue;
+ /* finished reading multi-part message */
+ if (new_msg->nlmsg_type == NLMSG_DONE)
+ break;
+
/* check that we support this message type */
r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
if (r < 0) {
if (r < 0)
return r;
- r = rtnl_rqueue_make_room(rtnl);
- if (r < 0)
- return r;
+ if (!first)
+ first = m;
+ else {
+ assert(previous);
- rtnl->rqueue[rtnl->rqueue_size ++] = m;
+ previous->next = m;
+ }
+ previous = m;
m = NULL;
+
ret += new_msg->nlmsg_len;
- /* reached end of multi-part message, or not a multi-part
- message at all */
- if (new_msg->nlmsg_type == NLMSG_DONE ||
- !(new_msg->nlmsg_flags & NLM_F_MULTI))
+ /* not a multi-part message, so stop reading*/
+ if (!(new_msg->nlmsg_flags & NLM_F_MULTI))
break;
}
+ r = rtnl_rqueue_make_room(rtnl);
+ if (r < 0)
+ return r;
+
+ rtnl->rqueue[rtnl->rqueue_size ++] = first;
+ first = NULL;
+
return ret;
}
m->sealed = true;
}
+
+sd_rtnl_message *sd_rtnl_message_next(sd_rtnl_message *m) {
+ assert_return(m, NULL);
+
+ return m->next;
+}