diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 19 | ||||
-rw-r--r-- | src/libsystemd-rtnl/rtnl-internal.h | 40 | ||||
-rw-r--r-- | src/libsystemd-rtnl/rtnl-message.c | 448 | ||||
-rw-r--r-- | src/libsystemd-rtnl/sd-rtnl.c | 188 | ||||
-rw-r--r-- | src/libsystemd-rtnl/test-rtnl.c | 126 | ||||
-rw-r--r-- | src/systemd/sd-rtnl.h | 52 |
7 files changed, 874 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore index d3cf8ef264..dd6432c972 100644 --- a/.gitignore +++ b/.gitignore @@ -135,6 +135,7 @@ /test-path-util /test-prioq /test-replace-var +/test-rtnl /test-sched-prio /test-sleep /test-strip-tab-ansi diff --git a/Makefile.am b/Makefile.am index e61910064d..6ce4b7fffa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -621,6 +621,25 @@ EXTRA_DIST += \ # ------------------------------------------------------------------------------ noinst_LTLIBRARIES += \ + libsystemd-rtnl.la + +libsystemd_rtnl_la_SOURCES = \ + src/systemd/sd-rtnl.h \ + src/libsystemd-rtnl/sd-rtnl.c \ + src/libsystemd-rtnl/rtnl-internal.h \ + src/libsystemd-rtnl/rtnl-message.c + +test_rtnl_SOURCES = \ + src/libsystemd-rtnl/test-rtnl.c + +test_rtnl_LDADD = \ + libsystemd-shared.la \ + libsystemd-rtnl.la + +tests += test-rtnl + +# ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ libsystemd-shared.la libsystemd_shared_la_SOURCES = \ diff --git a/src/libsystemd-rtnl/rtnl-internal.h b/src/libsystemd-rtnl/rtnl-internal.h new file mode 100644 index 0000000000..37b1d3d021 --- /dev/null +++ b/src/libsystemd-rtnl/rtnl-internal.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "refcnt.h" + +struct sd_rtnl { + RefCount n_ref; + + int fd; + + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sockaddr; + + unsigned serial; +}; +int message_get_errno(sd_rtnl_message *m); +int message_get_serial(sd_rtnl_message *m); +int message_seal(sd_rtnl *nl, sd_rtnl_message *m); +int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m); +int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret); diff --git a/src/libsystemd-rtnl/rtnl-message.c b/src/libsystemd-rtnl/rtnl-message.c new file mode 100644 index 0000000000..82ec1c97ab --- /dev/null +++ b/src/libsystemd-rtnl/rtnl-message.c @@ -0,0 +1,448 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/in.h> +#include <netinet/ether.h> +#include <stdbool.h> +#include <unistd.h> + +#include "util.h" +#include "refcnt.h" + +#include "sd-rtnl.h" +#include "rtnl-internal.h" + +struct sd_rtnl_message { + RefCount n_ref; + + struct nlmsghdr *hdr; + + struct rtattr *next_rta; + size_t remaining_size; + + bool sealed:1; +}; + +static int message_new(sd_rtnl_message **ret, size_t initial_size) { + sd_rtnl_message *m; + + assert_return(ret, -EINVAL); + assert_return(initial_size > 0, -EINVAL); + + m = new0(sd_rtnl_message, 1); + if (!m) + return -ENOMEM; + + m->hdr = calloc(initial_size, 1); + if (!m->hdr) { + free(m); + return -ENOMEM; + } + + m->n_ref = REFCNT_INIT; + + m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + m->sealed = false; + + *ret = m; + + return 0; +} + +int sd_rtnl_message_link_new(__u16 nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) { + struct ifinfomsg *ifi; + int r; + + assert_return(nlmsg_type == RTM_NEWLINK || nlmsg_type == RTM_DELLINK || nlmsg_type == RTM_GETLINK, -EINVAL); + assert_return(index > 0, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg))); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + (*ret)->hdr->nlmsg_type = nlmsg_type; + + ifi = (struct ifinfomsg *) NLMSG_DATA((*ret)->hdr); + + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; + ifi->ifi_type = type; + ifi->ifi_flags = flags; + ifi->ifi_change = 0xffffffff; + + return 0; +} + +int sd_rtnl_message_addr_new(__u16 nlmsg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret) { + struct ifaddrmsg *ifa; + int r; + + assert_return(nlmsg_type == RTM_NEWADDR || nlmsg_type == RTM_DELADDR || nlmsg_type == RTM_GETADDR, -EINVAL); + assert_return(index > 0, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg))); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + (*ret)->hdr->nlmsg_type = nlmsg_type; + + ifa = (struct ifaddrmsg *) NLMSG_DATA((*ret)->hdr); + + ifa->ifa_family = family; + ifa->ifa_prefixlen = prefixlen; + ifa->ifa_flags = flags; + ifa->ifa_scope = scope; + ifa->ifa_index = index; + + return 0; +} + +sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) { + if (m) + assert_se(REFCNT_INC(m->n_ref) >= 2); + + return m; +} + +sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) { + if (m && REFCNT_DEC(m->n_ref) <= 0) { + free(m->hdr); + free(m); + } + + return NULL; +} + +int sd_rtnl_message_get_type(sd_rtnl_message *m, __u16 *type) { + assert_return(m, -EINVAL); + assert_return(type, -EINVAL); + + *type = m->hdr->nlmsg_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) { + __u32 rta_length, message_length; + struct nlmsghdr *new_hdr; + struct rtattr *rta; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_length > 0, -EINVAL); + + /* get the size of the new rta attribute (without padding at the end) */ + rta_length = RTA_LENGTH(data_length); + /* get the new message size (with padding between the old message and the new attrib, + * but no padding after) + */ + message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length); + + /* realloc to fit the new attribute */ + new_hdr = realloc(m->hdr, message_length); + if (!new_hdr) + return -ENOMEM; + m->hdr = new_hdr; + + /* get pointer to the attribute we are about to add */ + rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len); + /* update message size */ + m->hdr->nlmsg_len = message_length; + + /* fill in the attribute */ + rta->rta_type = type; + rta->rta_len = rta_length; + /* we don't deal with the case where the user lies about the type and gives us + * too little data (so don't do that) + */ + memcpy(RTA_DATA(rta), data, data_length); + + return 0; +} + +int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) { + __u16 rtm_type; + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(data, -EINVAL); + + sd_rtnl_message_get_type(m, &rtm_type); + + switch (rtm_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + switch (type) { + case IFLA_IFNAME: + case IFLA_QDISC: + return add_rtattr(m, type, data, strlen(data) + 1); + case IFLA_MTU: + return add_rtattr(m, type, data, sizeof(unsigned int)); + case IFLA_LINK: + return add_rtattr(m, type, data, sizeof(int)); + case IFLA_STATS: + return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats)); + case IFLA_ADDRESS: + case IFLA_BROADCAST: + return add_rtattr(m, type, data, ETH_ALEN); + default: + return -ENOTSUP; + } + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + switch (type) { + case IFA_LABEL: + return add_rtattr(m, type, data, strlen(data) + 1); + case IFA_ADDRESS: + case IFA_LOCAL: + case IFA_BROADCAST: + case IFA_ANYCAST: + ifa = NLMSG_DATA(m->hdr); + switch (ifa->ifa_family) { + case AF_INET: + return add_rtattr(m, type, data, sizeof(struct in_addr)); + case AF_INET6: + return add_rtattr(m, type, data, sizeof(struct in6_addr)); + default: + return -EINVAL; + } + default: + return -ENOTSUP; + } + default: + return -ENOTSUP; + } +} + +static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) { + assert_return(m, -EINVAL); + assert_return(data, -EINVAL); + + if (!RTA_OK(m->next_rta, m->remaining_size)) + return 0; + + *data = RTA_DATA(m->next_rta); + *type = m->next_rta->rta_type; + + m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size); + + return 1; +} + +int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) { + __u16 rtm_type; + + assert_return(m, -EINVAL); + assert_return(data, -EINVAL); + + sd_rtnl_message_get_type(m, &rtm_type); + + switch (rtm_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + if (!m->next_rta) { + struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(m->hdr); + + m->next_rta = IFLA_RTA(ifi); + m->remaining_size = IFLA_PAYLOAD(m->hdr); + } + break;; + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + if (!m->next_rta) { + struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(m->hdr); + + m->next_rta = IFA_RTA(ifa); + m->remaining_size = IFLA_PAYLOAD(m->hdr); + } + break;; + default: + return -ENOTSUP; + } + + return message_read(m, type, data); +} + +int message_get_serial(sd_rtnl_message *m) { + assert(m); + + return m->hdr->nlmsg_seq; +} + +int message_get_errno(sd_rtnl_message *m) { + struct nlmsgerr *err; + + assert(m); + + if (m->hdr->nlmsg_type != NLMSG_ERROR) + return 0; + + err = NLMSG_DATA(m->hdr); + + return err->error; +} + +int message_seal(sd_rtnl *nl, sd_rtnl_message *m) { + if (m->sealed) + return -EPERM; + + m->hdr->nlmsg_seq = nl->serial++; + m->sealed = true; + + return 0; +} + +static int message_receive_need(sd_rtnl *rtnl, size_t *need) { + assert_return(rtnl, -EINVAL); + assert_return(need, -EINVAL); + + /* ioctl(rtnl->fd, FIONREAD, &need) + Does not appear to work on netlink sockets. libnl uses + MSG_PEEK instead. I don't know if that is worth the + extra roundtrip. + + For now we simply use the maximum message size the kernel + may use (NLMSG_GOODSIZE), and then realloc to the actual + size after reading the message (hence avoiding huge memory + usage in case many small messages are kept around) */ + *need = getpagesize(); + if (*need > 8192UL) + *need = 8192UL; + + return 0; +} + +/* returns the number of bytes sent, or a negative error code */ +int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { + ssize_t k; + + assert_return(nl, -EINVAL); + assert_return(m, -EINVAL); + + k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, + 0, &nl->sockaddr.sa, sizeof(nl->sockaddr)); + if (k < 0) + return (errno == EAGAIN) ? 0 : -errno; + + return k; +} + +/* 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. + * On failure, a negative error code is returned. +*/ +int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) { + sd_rtnl_message *m; + socklen_t addr_len = sizeof(nl->sockaddr); + int r; + ssize_t k; + size_t need; + + assert_return(nl, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_receive_need(nl, &need); + if (r < 0) + return r; + + r = message_new(&m, need); + if (r < 0) + return r; + + r = message_receive_need(nl, &need); + if (r < 0) + return r; + + m->hdr = realloc(m->hdr, need); + if (!m->hdr) + return -ENOMEM; + + k = recvfrom(nl->fd, m->hdr, need, + 0, &nl->sockaddr.sa, &addr_len); + if (k < 0) + k = (errno == EAGAIN) ? 0 : -errno; /* no data? weird... */ + else if (k == 0) + k = -ECONNRESET; /* connection was closed by the kernel */ + else if (addr_len != sizeof(nl->sockaddr.nl) || + nl->sockaddr.nl.nl_family != AF_NETLINK) + k = -EIO; /* not a netlink message */ + else if (nl->sockaddr.nl.nl_pid != 0) + k = 0; /* not from the kernel */ + else if ((size_t) k < sizeof(struct nlmsghdr) || + (size_t) k < m->hdr->nlmsg_len) + k = -EIO; /* too small (we do accept too big though) */ + else if (m->hdr->nlmsg_type == NLMSG_NOOP) + k = 0; + else if (m->hdr->nlmsg_type == NLMSG_ERROR && + m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + k = -EIO; + else if ((pid_t) m->hdr->nlmsg_pid != getpid()) + k = 0; /* not for us */ + + if (k > 0) + switch (m->hdr->nlmsg_type) { + /* check that the size matches the message type */ + case NLMSG_ERROR: + if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + k = -EIO; + break;; + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) + k = -EIO; + break;; + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg))) + k = -EIO; + break;; + case NLMSG_NOOP: + k = 0; + break;; + default: + k = 0; /* ignoring message of unknown type */ + } + + if (k <= 0) + sd_rtnl_message_unref(m); + else { + /* we probably allocated way too much memory, give it back */ + m->hdr = realloc(m->hdr, m->hdr->nlmsg_len); + *ret = m; + } + + return k; +} diff --git a/src/libsystemd-rtnl/sd-rtnl.c b/src/libsystemd-rtnl/sd-rtnl.c new file mode 100644 index 0000000000..abf45d0eed --- /dev/null +++ b/src/libsystemd-rtnl/sd-rtnl.c @@ -0,0 +1,188 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/socket.h> +#include <poll.h> + +#include "macro.h" +#include "util.h" + +#include "sd-rtnl.h" +#include "rtnl-internal.h" + +static int sd_rtnl_new(sd_rtnl **ret) { + sd_rtnl *rtnl; + + assert_return(ret, -EINVAL); + + rtnl = new0(sd_rtnl, 1); + if (!rtnl) + return -ENOMEM; + + rtnl->n_ref = REFCNT_INIT; + + rtnl->fd = -1; + + rtnl->sockaddr.nl.nl_family = AF_NETLINK; + + *ret = rtnl; + return 0; +} + +int sd_rtnl_open(__u32 groups, sd_rtnl **ret) { + sd_rtnl *rtnl; + int r; + + r = sd_rtnl_new(&rtnl); + if (r < 0) + return r; + + rtnl->fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); + if (rtnl->fd < 0) { + r = -errno; + sd_rtnl_unref(rtnl); + return r; + } + + rtnl->sockaddr.nl.nl_groups = groups; + + r = bind(rtnl->fd, &rtnl->sockaddr.sa, sizeof(rtnl->sockaddr)); + if (r < 0) { + r = -errno; + sd_rtnl_unref(rtnl); + return r; + } + + *ret = rtnl; + + return 0; +} + +sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { + if (rtnl) + assert_se(REFCNT_INC(rtnl->n_ref) >= 2); + + return rtnl; +} + +sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) { + if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) { + if (rtnl->fd >= 0) + close_nointr_nofail(rtnl->fd); + free(rtnl); + } + + return NULL; +} + +int sd_rtnl_send_with_reply_and_block(sd_rtnl *nl, + sd_rtnl_message *message, + uint64_t usec, + sd_rtnl_message **ret) { + struct pollfd p[1] = {}; + sd_rtnl_message *reply; + struct timespec left; + usec_t timeout; + int r, serial; + + assert_return(nl, -EINVAL); + assert_return(message, -EINVAL); + + r = message_seal(nl, message); + if (r < 0) + return r; + + serial = message_get_serial(message); + + p[0].fd = nl->fd; + p[0].events = POLLOUT; + + timeout = now(CLOCK_MONOTONIC) + usec; + + for (;;) { + if (usec != (uint64_t) -1) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (n >= timeout) + return -ETIMEDOUT; + + timespec_store(&left, timeout - n); + } + + r = ppoll(p, 1, usec == (uint64_t) -1 ? NULL : &left, NULL); + if (r < 0) + return 0; + + r = socket_write_message(nl, message); + if (r < 0) + return r; + + if (r > 0) { + break; + } + } + + p[0].events = POLLIN; + + for (;;) { + if (usec != (uint64_t) -1) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (n >= timeout) + return -ETIMEDOUT; + + timespec_store(&left, timeout - n); + } + + r = ppoll(p, 1, usec == (uint64_t) -1 ? NULL : &left, NULL); + if (r < 0) + return r; + + r = socket_read_message(nl, &reply); + if (r < 0) + return r; + + if (r > 0) { + int received_serial = message_get_serial(reply); + + if (received_serial == serial) { + r = message_get_errno(reply); + if (r < 0) { + sd_rtnl_message_unref(reply); + return r; + } + + if (ret) + *ret = reply; + else + reply = sd_rtnl_message_unref(reply); + + break;; + } + + reply = sd_rtnl_message_unref(reply); + } + } + + return 0; +} diff --git a/src/libsystemd-rtnl/test-rtnl.c b/src/libsystemd-rtnl/test-rtnl.c new file mode 100644 index 0000000000..bcfd816688 --- /dev/null +++ b/src/libsystemd-rtnl/test-rtnl.c @@ -0,0 +1,126 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/ether.h> + +#include "util.h" +#include "macro.h" +#include "sd-rtnl.h" +#include "socket-util.h" + +static void test_link_configure(sd_rtnl *rtnl, int ifindex) { + _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message; + __u16 type; + const char *mac = "98:fe:94:3f:c6:18", *name = "test"; + unsigned int mtu = 1450; + void *data; + + /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ + assert(sd_rtnl_message_link_new(RTM_GETLINK, ifindex, 0, 0, &message) >= 0); + assert(sd_rtnl_message_append(message, IFLA_IFNAME, name) >= 0); + assert(sd_rtnl_message_append(message, IFLA_ADDRESS, ether_aton(mac)) >= 0); + assert(sd_rtnl_message_append(message, IFLA_MTU, &mtu) >= 0); + + assert(sd_rtnl_message_read(message, &type, &data) >= 0); + assert(type == IFLA_IFNAME); + assert(streq(name, (char *) data)); + + assert(sd_rtnl_message_read(message, &type, &data) >= 0); + assert(type == IFLA_ADDRESS); + assert(streq(mac, ether_ntoa(data))); + + assert(sd_rtnl_message_read(message, &type, &data) >= 0); + assert(type == IFLA_MTU); + assert(mtu == *(unsigned int *) data); + + /* let's assume that this test is always ran when the loopback device is up, so that it will fail */ + assert(sd_rtnl_send_with_reply_and_block(rtnl, message, 2 * USEC_PER_SEC, NULL) == 0); +} + +int main(void) { + sd_rtnl *rtnl; + sd_rtnl_message *m; + sd_rtnl_message *r; + void *data; + int if_loopback; + __u16 type; + unsigned int mtu = 0; + unsigned int *mtu_reply; + + assert(sd_rtnl_open(0, &rtnl) >= 0); + assert(rtnl); + + if_loopback = (int) if_nametoindex("lo"); + assert(if_loopback > 0); + + test_link_configure(rtnl, if_loopback); + + assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, 0, 0, &m) >= 0); + assert(m); + + assert(sd_rtnl_message_get_type(m, &type) >= 0); + assert(type == RTM_GETLINK); + + assert(sd_rtnl_message_read(m, &type, &data) == 0); + + assert(sd_rtnl_send_with_reply_and_block(rtnl, m, 100000000, &r) >= 0); + assert(sd_rtnl_message_get_type(r, &type) >= 0); + assert(type == RTM_NEWLINK); + + assert(sd_rtnl_message_read(m, &type, data) == 0); + assert((r = sd_rtnl_message_unref(r)) == NULL); + + assert(sd_rtnl_send_with_reply_and_block(rtnl, m, -1, &r) == -EPERM); + assert((m = sd_rtnl_message_unref(m)) == NULL); + assert((r = sd_rtnl_message_unref(r)) == NULL); + + assert(sd_rtnl_message_link_new(RTM_GETLINK, if_loopback, 0, 0, &m) >= 0); + assert(m); + + assert(sd_rtnl_message_append(m, IFLA_MTU, &mtu) >= 0); + assert(sd_rtnl_message_read(m, &type, (void **) &mtu_reply) == 1); + + assert(type == IFLA_MTU); + assert(*mtu_reply == 0); + + assert(sd_rtnl_message_read(m, &type, data) == 0); + + assert(sd_rtnl_send_with_reply_and_block(rtnl, m, -1, &r) >= 0); + while (sd_rtnl_message_read(r, &type, &data)) { + switch (type) { + case IFLA_MTU: + assert(*(unsigned int *) data == 65536); + break;; + case IFLA_IFNAME: + assert(streq((char *) data, "lo")); + break;; + case IFLA_QDISC: + assert(streq((char *) data, "noqueue")); + break;; + } + } + + assert((m = sd_rtnl_message_unref(m)) == NULL); + assert((r = sd_rtnl_message_unref(r)) == NULL); + assert((rtnl = sd_rtnl_unref(rtnl)) == NULL); + + return EXIT_SUCCESS; +} diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h new file mode 100644 index 0000000000..5f5f3e3d20 --- /dev/null +++ b/src/systemd/sd-rtnl.h @@ -0,0 +1,52 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <linux/rtnetlink.h> +#include <linux/netlink.h> +#include <stdint.h> +#include <util.h> + +typedef struct sd_rtnl sd_rtnl; +typedef struct sd_rtnl_message sd_rtnl_message; + +/* bus */ +int sd_rtnl_open(__u32 groups, sd_rtnl **nl); + +sd_rtnl *sd_rtnl_ref(sd_rtnl *nl); +sd_rtnl *sd_rtnl_unref(sd_rtnl *nl); + +int sd_rtnl_send_with_reply_and_block(sd_rtnl *nl, sd_rtnl_message *message, uint64_t timeout, sd_rtnl_message **reply); + +/* messages */ +int sd_rtnl_message_link_new(__u16 msg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret); +int sd_rtnl_message_addr_new(__u16 msg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret); +sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m); +sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m); + +int sd_rtnl_message_get_type(sd_rtnl_message *m, __u16 *type); +int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data); +int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data); + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl*, sd_rtnl_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl_message*, sd_rtnl_message_unref); + +#define _cleanup_sd_rtnl_unref_ _cleanup_(sd_rtnl_unrefp) +#define _cleanup_sd_rtnl_message_unref_ _cleanup_(sd_rtnl_message_unrefp) |