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=84b46afb6b1807ed8d2a07d27b469d4c63147480;hp=9043c675acbb95877e7f74c2038374c5d6149b69;hb=66269b05cf31ef5d8f83a7150c314bf35ee8a35e;hpb=7ca1d31964a2553f7bd011bc10ac42e0ebc1f975 diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 9043c675a..84b46afb6 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -290,6 +290,21 @@ int sd_rtnl_message_addr_get_family(sd_rtnl_message *m, unsigned char *family) { return 0; } +int sd_rtnl_message_addr_get_prefixlen(sd_rtnl_message *m, unsigned char *prefixlen) { + 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(prefixlen, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *prefixlen = ifa->ifa_prefixlen; + + return 0; +} + int sd_rtnl_message_addr_get_scope(sd_rtnl_message *m, unsigned char *scope) { struct ifaddrmsg *ifa; @@ -1083,6 +1098,60 @@ 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 */ + 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. @@ -1090,16 +1159,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; @@ -1110,13 +1172,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; @@ -1129,13 +1187,10 @@ 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; @@ -1143,24 +1198,6 @@ int socket_read_message(sd_rtnl *rtnl) { /* 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 && - 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 (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { multi_part = true;