X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-rtnl%2Frtnl-message.c;h=3a24cb885f36a5d8b51d118f4ae6e5448fb806ac;hp=4c746f9d8afe9294233af1777f572b3145d62413;hb=1403f45ab61d6f2026a3a7a06b52a536c1b7a3b3;hpb=d8e538ecd9e62f841242f07e3df5c835c1ba6313 diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 4c746f9d8..3a24cb885 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -42,11 +42,10 @@ #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 @@ -57,15 +56,8 @@ static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initia 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; @@ -74,6 +66,7 @@ static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initia } 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; @@ -84,15 +77,25 @@ int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t type) { 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; } @@ -256,6 +259,66 @@ int sd_rtnl_message_addr_set_scope(sd_rtnl_message *m, unsigned char scope) { return 0; } +int sd_rtnl_message_addr_get_family(sd_rtnl_message *m, unsigned char *family) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *family = ifa->ifa_family; + + return 0; +} + +int sd_rtnl_message_addr_get_scope(sd_rtnl_message *m, unsigned char *scope) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(scope, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *scope = ifa->ifa_scope; + + return 0; +} + +int sd_rtnl_message_addr_get_flags(sd_rtnl_message *m, unsigned char *flags) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *flags = ifa->ifa_flags; + + return 0; +} + +int sd_rtnl_message_addr_get_ifindex(sd_rtnl_message *m, int *ifindex) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(ifindex, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *ifindex = ifa->ifa_index; + + return 0; +} + int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index, unsigned char family) { @@ -286,6 +349,19 @@ int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, 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); @@ -302,6 +378,8 @@ sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) { for (i = 0; i <= m->n_containers; i++) free(m->rta_offset_tb[i]); + sd_rtnl_message_unref(m->next); + free(m); } @@ -559,6 +637,24 @@ int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, c 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; @@ -741,6 +837,25 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str 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; @@ -911,19 +1026,20 @@ int rtnl_message_parse(sd_rtnl_message *m, 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 (type > max) { - log_debug("rtnl: message parse - ignore out of range attribute type"); + /* if the kernel is newer than the headers we used + when building, we ignore out-of-range attributes + */ + if (type > max) continue; - } if (tb[type]) log_debug("rtnl: message parse - overwriting repeated attribute"); @@ -963,83 +1079,133 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { * 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_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 = { + .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; + /* 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 == -ENOTSUP) + log_debug("sd-rtnl: ignored message with unknown type: %u", + new_msg->nlmsg_type); + + continue; + } + + /* 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); + /* seal and parse the top-level message */ + r = sd_rtnl_message_rewind(m); + if (r < 0) + return r; + + if (!first) + first = m; + else { + assert(previous); + + previous->next = m; + } + previous = m; + m = NULL; + + ret += new_msg->nlmsg_len; + + /* 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; - *ret = m; - m = NULL; + rtnl->rqueue[rtnl->rqueue_size ++] = first; + first = NULL; - return len; + return ret; } int sd_rtnl_message_rewind(sd_rtnl_message *m) { @@ -1084,7 +1250,8 @@ int sd_rtnl_message_rewind(sd_rtnl_message *m) { &m->rta_offset_tb[m->n_containers], &m->rta_tb_size[m->n_containers], type_system->max, - (char*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(type->size), + (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + + NLMSG_ALIGN(type->size)), NLMSG_PAYLOAD(m->hdr, type->size)); if (r < 0) return r; @@ -1099,3 +1266,9 @@ void rtnl_message_seal(sd_rtnl_message *m) { m->sealed = true; } + +sd_rtnl_message *sd_rtnl_message_next(sd_rtnl_message *m) { + assert_return(m, NULL); + + return m->next; +}