X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-rtnl%2Frtnl-message.c;h=e5c72e765361b52edf395310195716b452234378;hp=fb429864b94c09776433d4b82f1feea3318b8569;hb=64918838d566058b7cec72d13f4524de0a64a8e3;hpb=127dc4ea9487397b1ab70447e2f44d091e44ab5f diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index fb429864b..e5c72e765 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -23,12 +23,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include "util.h" #include "refcnt.h" @@ -42,6 +36,8 @@ #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; +#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) + static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) { sd_rtnl_message *m; @@ -132,8 +128,24 @@ int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) { return 0; } +int sd_rtnl_message_route_get_family(sd_rtnl_message *m, int *family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; +} + int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, - uint16_t nlmsg_type, unsigned char rtm_family) { + uint16_t nlmsg_type, int rtm_family, + unsigned char rtm_protocol) { struct rtmsg *rtm; int r; @@ -154,7 +166,7 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, rtm->rtm_scope = RT_SCOPE_UNIVERSE; rtm->rtm_type = RTN_UNICAST; rtm->rtm_table = RT_TABLE_MAIN; - rtm->rtm_protocol = RTPROT_BOOT; + rtm->rtm_protocol = rtm_protocol; return 0; } @@ -189,6 +201,20 @@ int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) { return 0; } +int sd_rtnl_message_link_set_family(sd_rtnl_message *m, unsigned family) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_family = family; + + return 0; +} + int sd_rtnl_message_new_link(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index) { struct ifinfomsg *ifi; @@ -275,7 +301,7 @@ 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) { +int sd_rtnl_message_addr_get_family(sd_rtnl_message *m, int *family) { struct ifaddrmsg *ifa; assert_return(m, -EINVAL); @@ -352,7 +378,7 @@ int sd_rtnl_message_addr_get_ifindex(sd_rtnl_message *m, int *ifindex) { int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index, - unsigned char family) { + int family) { struct ifaddrmsg *ifa; int r; @@ -383,7 +409,7 @@ int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, } int sd_rtnl_message_new_addr_update(sd_rtnl *rtnl, sd_rtnl_message **ret, - int index, unsigned char family) { + int index, int family) { int r; r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family); @@ -464,6 +490,21 @@ int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) { return 0; } +int sd_rtnl_message_link_get_type(sd_rtnl_message *m, unsigned *type) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(type, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *type = ifi->ifi_type; + + return 0; +} + /* If successful the updated message will be correctly aligned, if unsuccessful the old message is untouched. */ static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) { @@ -515,7 +556,6 @@ static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, /* if no data was passed, make sure we still initialize the padding note that we can have data_length > 0 (used by some containers) */ padding = RTA_DATA(rta); - data_length = 0; } /* make sure also the padding at the end of the message is initialized */ @@ -714,7 +754,7 @@ int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) { if (r < 0) return r; - r = add_rtattr(m, type, NULL, size); + r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); if (r < 0) return r; @@ -786,10 +826,12 @@ int rtnl_message_read_internal(sd_rtnl_message *m, unsigned short type, void **d return RTA_PAYLOAD(rta); } -int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, char **data) { +int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, const char **data) { int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_STRING); if (r < 0) return r; @@ -800,7 +842,8 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, char ** else if (strnlen(attr_data, r) >= (size_t) r) return -EIO; - *data = (char *) attr_data; + if (data) + *data = (const char *) attr_data; return 0; } @@ -809,6 +852,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U8); if (r < 0) return r; @@ -819,7 +864,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da else if ((size_t) r < sizeof(uint8_t)) return -EIO; - *data = *(uint8_t *) attr_data; + if (data) + *data = *(uint8_t *) attr_data; return 0; } @@ -828,6 +874,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t * int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U16); if (r < 0) return r; @@ -838,7 +886,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t * else if ((size_t) r < sizeof(uint16_t)) return -EIO; - *data = *(uint16_t *) attr_data; + if (data) + *data = *(uint16_t *) attr_data; return 0; } @@ -847,6 +896,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t * int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U32); if (r < 0) return r; @@ -857,7 +908,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t * else if ((size_t)r < sizeof(uint32_t)) return -EIO; - *data = *(uint32_t *) attr_data; + if (data) + *data = *(uint32_t *) attr_data; return 0; } @@ -866,6 +918,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_ETHER_ADDR); if (r < 0) return r; @@ -876,7 +930,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str else if ((size_t)r < sizeof(struct ether_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct ether_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct ether_addr)); return 0; } @@ -885,6 +940,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_CACHE_INFO); if (r < 0) return r; @@ -895,7 +952,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str else if ((size_t)r < sizeof(struct ifa_cacheinfo)) return -EIO; - memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); + if (info) + memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); return 0; } @@ -904,6 +962,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; @@ -914,7 +974,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct else if ((size_t)r < sizeof(struct in_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct in_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct in_addr)); return 0; } @@ -923,6 +984,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; @@ -933,7 +996,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc else if ((size_t)r < sizeof(struct in6_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct in6_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct in6_addr)); return 0; } @@ -962,7 +1026,7 @@ int sd_rtnl_message_enter_container(sd_rtnl_message *m, unsigned short type) { return r; } else if (nl_type->type == NLA_UNION) { const NLTypeSystemUnion *type_system_union; - char *key; + const char *key; r = type_system_get_type_system_union(m->container_type_system[m->n_containers], &type_system_union, @@ -1057,7 +1121,7 @@ int rtnl_message_parse(sd_rtnl_message *m, *rta_tb_size = max + 1; for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { - type = rta->rta_type; + type = RTA_TYPE(rta); /* if the kernel is newer than the headers we used when building, we ignore out-of-range attributes @@ -1098,6 +1162,63 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { return k; } +static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { + uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct nl_pktinfo))]; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = cred_buffer, + .msg_controllen = sizeof(cred_buffer), + }; + struct cmsghdr *cmsg; + uint32_t group = 0; + bool auth = false; + int r; + + assert(fd >= 0); + assert(iov); + + r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); + if (r < 0) { + /* no data */ + if (errno == ENOBUFS) + log_debug("rtnl: kernel receive buffer overrun"); + + return (errno == EAGAIN) ? 0 : -errno; + } else if (r == 0) + /* connection was closed by the kernel */ + return -ECONNRESET; + + 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; + } else if (cmsg->cmsg_level == SOL_NETLINK && + cmsg->cmsg_type == NETLINK_PKTINFO && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { + struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); + + /* multi-cast group */ + group = pktinfo->group; + } + } + + if (!auth) + /* not from the kernel, ignore */ + return 0; + + if (group) + *_group = group; + + return r; +} + /* On success, the number of bytes received is returned and *ret points to the received message * which has a valid header and the correct size. * If nothing useful was received 0 is returned. @@ -1105,16 +1226,9 @@ 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; - 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, multi_part = false, done = false; + uint32_t group = 0; + bool multi_part = false, done = false; struct nlmsghdr *new_msg; size_t len; int r; @@ -1125,13 +1239,9 @@ int socket_read_message(sd_rtnl *rtnl) { assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); /* read nothing, just get the pending message size */ - r = recvmsg(rtnl->fd, &msg, MSG_PEEK | MSG_TRUNC); - if (r < 0) - /* no data */ - return (errno == EAGAIN) ? 0 : -errno; - else if (r == 0) - /* connection was closed by the kernel */ - return -ECONNRESET; + r = socket_recv_message(rtnl->fd, &iov, &group, true); + if (r <= 0) + return r; else len = (size_t)r; @@ -1144,34 +1254,13 @@ int socket_read_message(sd_rtnl *rtnl) { iov.iov_base = rtnl->rbuffer; iov.iov_len = rtnl->rbuffer_allocated; - r = recvmsg(rtnl->fd, &msg, MSG_TRUNC); - if (r < 0) - /* no data */ - return (errno == EAGAIN) ? 0 : -errno; - else if (r == 0) - /* connection was closed by the kernel */ - return -ECONNRESET; + /* read the pending message */ + r = socket_recv_message(rtnl->fd, &iov, &group, false); + if (r <= 0) + return r; else 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; - } - } - } - - if (!auth) - /* not from the kernel, ignore */ - return 0; - if (len > rtnl->rbuffer_allocated) /* message did not fit in read buffer */ return -EIO; @@ -1192,7 +1281,7 @@ int socket_read_message(sd_rtnl *rtnl) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; const NLType *nl_type; - if (new_msg->nlmsg_pid && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid) + if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid) /* not broadcast and not for us */ continue; @@ -1217,8 +1306,10 @@ int socket_read_message(sd_rtnl *rtnl) { } /* check that the size matches the message type */ - if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) + if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) { + log_debug("sd-rtnl: message larger than expected, dropping"); continue; + } r = message_new_empty(rtnl, &m); if (r < 0)