#include <netinet/ether.h>
#include <stdbool.h>
#include <unistd.h>
-#include <linux/netlink.h>
-#include <linux/veth.h>
-#include <linux/if.h>
-#include <linux/ip.h>
-#include <linux/if_tunnel.h>
-#include <linux/if_bridge.h>
#include "util.h"
#include "refcnt.h"
#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;
}
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;
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;
}
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;
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);
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;
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;
}
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);
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) {
/* 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 */
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;
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;
else if (strnlen(attr_data, r) >= (size_t) r)
return -EIO;
- *data = (char *) attr_data;
+ *data = (const char *) attr_data;
return 0;
}
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,
*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
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.
*/
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;