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;
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.
*/
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;
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;
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;
/* 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;
_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;