#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offsets[i]) : NULL)
#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
-static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initial_size) {
+static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) {
sd_rtnl_message *m;
assert_return(ret, -EINVAL);
- assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
/* Note that 'rtnl' is curretly unused, if we start using it internally
we must take care to avoid problems due to mutual references between
if (!m)
return -ENOMEM;
- m->hdr = malloc0(initial_size);
- if (!m->hdr) {
- free(m);
- return -ENOMEM;
- }
-
m->n_ref = REFCNT_INIT;
- m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
m->sealed = false;
*ret = m;
}
int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t type) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
const NLType *nl_type;
size_t size;
int r;
assert(nl_type->type == NLA_NESTED);
- size = NLMSG_SPACE(nl_type->size);
-
- r = message_new_empty(rtnl, ret, size);
+ r = message_new_empty(rtnl, &m);
if (r < 0)
return r;
- (*ret)->container_type_system[0] = nl_type->type_system;
- (*ret)->hdr->nlmsg_len = size;
- (*ret)->hdr->nlmsg_type = type;
+ size = NLMSG_SPACE(nl_type->size);
+
+ assert(size >= sizeof(struct nlmsghdr));
+ m->hdr = malloc0(size);
+ if (!m->hdr)
+ return -ENOMEM;
+
+ m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ m->container_type_system[0] = nl_type->type_system;
+ m->hdr->nlmsg_len = size;
+ m->hdr->nlmsg_type = type;
+
+ *ret = m;
+ m = NULL;
return 0;
}
return 0;
}
+int sd_rtnl_message_new_addr_update(sd_rtnl *rtnl, sd_rtnl_message **ret,
+ int index, unsigned char family) {
+ int r;
+
+ r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family);
+ if (r < 0)
+ return r;
+
+ (*ret)->hdr->nlmsg_flags |= NLM_F_REPLACE;
+
+ return 0;
+}
+
sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
if (m)
assert_se(REFCNT_INC(m->n_ref) >= 2);
return 0;
}
+int sd_rtnl_message_append_cache_info(sd_rtnl_message *m, unsigned short type, const struct ifa_cacheinfo *info) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(info, -EINVAL);
+
+ r = message_attribute_has_type(m, type, NLA_CACHE_INFO);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
size_t size;
int r;
return 0;
}
+int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, struct ifa_cacheinfo *info) {
+ int r;
+ void *attr_data;
+
+ r = message_attribute_has_type(m, type, NLA_CACHE_INFO);
+ if (r < 0)
+ return r;
+
+ r = rtnl_message_read_internal(m, type, &attr_data);
+ if (r < 0)
+ return r;
+ else if ((size_t)r < sizeof(struct ifa_cacheinfo))
+ return -EIO;
+
+ memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
+
+ return 0;
+}
+
int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct in_addr *data) {
int r;
void *attr_data;
unsigned short type;
size_t *tb;
- tb = (size_t *) new0(size_t *, max);
+ tb = new0(size_t, max + 1);
if(!tb)
return -ENOMEM;
- *rta_tb_size = max;
+ *rta_tb_size = max + 1;
for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) {
type = rta->rta_type;
* If nothing useful was received 0 is returned.
* On failure, a negative error code is returned.
*/
-int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
- _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
- const NLType *nl_type;
- struct nlmsghdr *new_hdr;
- union {
- struct sockaddr sa;
- struct sockaddr_nl nl;
- } addr;
- socklen_t addr_len;
+int socket_read_message(sd_rtnl *rtnl) {
+ _cleanup_free_ void *buffer = NULL;
+ uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
+ struct iovec iov = {};
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cred_buffer,
+ .msg_controllen = sizeof(cred_buffer),
+ };
+ struct cmsghdr *cmsg;
+ bool auth = false;
+ struct nlmsghdr *new_msg;
size_t need, len;
- int r;
+ int r, ret = 0;
- assert(nl);
- assert(ret);
+ assert(rtnl);
- r = message_receive_need(nl, &need);
+ r = message_receive_need(rtnl, &need);
if (r < 0)
return r;
- r = message_new_empty(nl, &m, need);
- if (r < 0)
- return r;
+ buffer = malloc0(need);
+ if (!buffer)
+ return -ENOMEM;
- addr_len = sizeof(addr);
+ iov.iov_base = buffer;
+ iov.iov_len = need;
- r = recvfrom(nl->fd, m->hdr, need,
- 0, &addr.sa, &addr_len);
+ r = recvmsg(rtnl->fd, &msg, 0);
if (r < 0)
- return (errno == EAGAIN) ? 0 : -errno; /* no data */
+ /* no data */
+ return (errno == EAGAIN) ? 0 : -errno;
else if (r == 0)
- return -ECONNRESET; /* connection was closed by the kernel */
- else if (addr_len != sizeof(addr.nl) ||
- addr.nl.nl_family != AF_NETLINK)
- return -EIO; /* not a netlink message */
- else if (addr.nl.nl_pid != 0)
- return 0; /* not from the kernel */
- else if ((size_t) r < sizeof(struct nlmsghdr) ||
- (size_t) r < m->hdr->nlmsg_len)
- return -EIO; /* too small (we do accept too big though) */
- else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
- return 0; /* not broadcast and not for us */
+ /* connection was closed by the kernel */
+ return -ECONNRESET;
else
- len = (size_t) r;
+ len = (size_t)r;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+ struct ucred *ucred = (void *)CMSG_DATA(cmsg);
+
+ /* from the kernel */
+ if (ucred->uid == 0 && ucred->pid == 0) {
+ auth = true;
+ break;
+ }
+ }
+ }
- /* silently drop noop messages */
- if (m->hdr->nlmsg_type == NLMSG_NOOP)
+ if (!auth)
+ /* not from the kernel, ignore */
return 0;
- /* check that we support this message type */
- r = type_system_get_type(NULL, &nl_type, m->hdr->nlmsg_type);
- if (r < 0) {
- if (r == -ENOTSUP)
- log_debug("sd-rtnl: ignored message with unknown type: %u",
- m->hdr->nlmsg_type);
+ for (new_msg = buffer; 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;
- return 0;
- }
+ if (new_msg->nlmsg_pid && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
+ /* not broadcast and not for us */
+ continue;
- /* check that the size matches the message type */
- if (len < NLMSG_LENGTH(nl_type->size))
- return -EIO;
+ /* silently drop noop messages */
+ if (new_msg->nlmsg_type == NLMSG_NOOP)
+ continue;
- /* we probably allocated way too much memory, give it back */
- new_hdr = realloc(m->hdr, len);
- if (!new_hdr)
- return -ENOMEM;
- m->hdr = new_hdr;
+ /* check that we support this message type */
+ r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
+ if (r < 0) {
+ if (r == -ENOTSUP)
+ log_debug("sd-rtnl: ignored message with unknown type: %u",
+ new_msg->nlmsg_type);
- /* seal and parse the top-level message */
- r = sd_rtnl_message_rewind(m);
- if (r < 0)
- return r;
+ continue;
+ }
- *ret = m;
- m = NULL;
+ /* check that the size matches the message type */
+ if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size))
+ continue;
+
+ r = message_new_empty(rtnl, &m);
+ if (r < 0)
+ return r;
+
+ m->hdr = memdup(new_msg, new_msg->nlmsg_len);
+ if (!m->hdr)
+ return -ENOMEM;
+
+ /* seal and parse the top-level message */
+ r = sd_rtnl_message_rewind(m);
+ if (r < 0)
+ return r;
+
+ r = rtnl_rqueue_make_room(rtnl);
+ if (r < 0)
+ return r;
+
+ rtnl->rqueue[rtnl->rqueue_size ++] = 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))
+ break;
+ }
- return len;
+ return ret;
}
int sd_rtnl_message_rewind(sd_rtnl_message *m) {