+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;
+}
+