This is a collection of the following fixes:
sd-ndisc: better validate RA packets - cddf4d8
sd-ndisc: always send the link-layer address - 6d06ac1
libsystemd-network: clean up recv functions - 0d43d2f
nd-ndisc: don't fail if src address is unset - d7fa438
diff -ruN systemd-228/src/libsystemd-network/icmp6-util.c systemd-228-new/src/libsystemd-network/icmp6-util.c
--- systemd-228/src/libsystemd-network/icmp6-util.c 2015-11-17 23:59:06.000000000 -0800
+++ systemd-228-new/src/libsystemd-network/icmp6-util.c 2016-01-06 14:46:34.855095186 -0800
@@ -47,17 +47,15 @@
.ipv6mr_interface = index,
};
_cleanup_close_ int s = -1;
- int r, zero = 0, hops = 255;
+ int r, zero = 0, one = 1, hops = 255;
- s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_ICMPV6);
+ s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
if (s < 0)
return -errno;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
- r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
- sizeof(filter));
+ r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
if (r < 0)
return -errno;
@@ -65,23 +63,23 @@
IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
Empirical experiments indicates otherwise and therefore an
IPV6_MULTICAST_IF socket option is used here instead */
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
- sizeof(index));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
- sizeof(zero));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
- sizeof(hops));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
- sizeof(mreq));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (r < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
if (r < 0)
return -errno;
@@ -101,25 +99,25 @@
struct ether_addr rs_opt_mac;
} _packed_ rs = {
.rs.nd_rs_type = ND_ROUTER_SOLICIT,
+ .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
+ .rs_opt.nd_opt_len = 1,
};
- struct iovec iov[1] = {
- { &rs, },
+ struct iovec iov = {
+ .iov_base = &rs,
+ .iov_len = sizeof(rs),
};
struct msghdr msg = {
.msg_name = &dst,
.msg_namelen = sizeof(dst),
- .msg_iov = iov,
+ .msg_iov = &iov,
.msg_iovlen = 1,
};
int r;
- if (ether_addr) {
- memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN);
- rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
- rs.rs_opt.nd_opt_len = 1;
- iov[0].iov_len = sizeof(rs);
- } else
- iov[0].iov_len = sizeof(rs.rs);
+ assert(s >= 0);
+ assert(ether_addr);
+
+ rs.rs_opt_mac = *ether_addr;
r = sendmsg(s, &msg, 0);
if (r < 0)
diff -ruN systemd-228/src/libsystemd-network/sd-ndisc.c systemd-228-new/src/libsystemd-network/sd-ndisc.c
--- systemd-228/src/libsystemd-network/sd-ndisc.c 2015-11-17 23:59:06.000000000 -0800
+++ systemd-228-new/src/libsystemd-network/sd-ndisc.c 2016-01-06 14:47:24.510383825 -0800
@@ -32,6 +32,7 @@
#include "in-addr-util.h"
#include "list.h"
#include "socket-util.h"
+#include "string-util.h"
#define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
#define NDISC_MAX_ROUTER_SOLICITATIONS 3
@@ -417,8 +418,7 @@
return 0;
}
-static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
- ssize_t len) {
+static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) {
void *opt;
struct nd_opt_hdr *opt_hdr;
@@ -481,30 +481,86 @@
static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_free_ struct nd_router_advert *ra = NULL;
sd_ndisc *nd = userdata;
- int r, buflen = 0, pref, stateful;
- union sockaddr_union router = {};
- socklen_t router_len = sizeof(router);
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_LEN(sizeof(int))];
+ } control = {};
+ struct iovec iov = {};
+ union sockaddr_union sa = {};
+ struct msghdr msg = {
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ struct in6_addr *gw;
unsigned lifetime;
ssize_t len;
+ int r, pref, stateful, buflen = 0;
assert(s);
assert(nd);
assert(nd->event);
r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = ICMP6_RECV_SIZE;
+ if (r < 0)
+ return -errno;
+ else if (buflen < 0)
+ /* This really should not happen */
+ return -EIO;
+
+ iov.iov_len = buflen;
- ra = malloc(buflen);
+ ra = malloc(iov.iov_len);
if (!ra)
return -ENOMEM;
- len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
+ iov.iov_base = ra;
+
+ len = recvmsg(fd, &msg, 0);
if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
+ return -errno;
+ } else if ((size_t)len < sizeof(struct nd_router_advert)) {
return 0;
- } else if (router_len != sizeof(router.in6) && router_len != 0) {
- log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
+ } else if (msg.msg_namelen == 0)
+ gw = NULL; /* only happens when running the test-suite over a socketpair */
+ else if (msg.msg_namelen != sizeof(sa.in6)) {
+ log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
+ return 0;
+ } else
+ gw = &sa.in6.sin6_addr;
+
+ assert(!(msg.msg_flags & MSG_CTRUNC));
+ assert(!(msg.msg_flags & MSG_TRUNC));
+
+ CMSG_FOREACH(cmsg, &msg) {
+ if (cmsg->cmsg_level == SOL_IPV6 &&
+ cmsg->cmsg_type == IPV6_HOPLIMIT &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ int hops = *(int*)CMSG_DATA(cmsg);
+
+ if (hops != 255) {
+ log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
+ return 0;
+ }
+
+ break;
+ }
+ }
+
+ if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
+ _cleanup_free_ char *addr = NULL;
+
+ (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
+
+ log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
return 0;
}
@@ -544,7 +600,7 @@
}
if (nd->router_callback)
- nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata);
+ nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
return 0;
}
@@ -552,8 +608,6 @@
static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ndisc *nd = userdata;
uint64_t time_now, next_timeout;
- struct ether_addr unset = { };
- struct ether_addr *addr = NULL;
int r;
assert(s);
@@ -567,10 +621,7 @@
nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
} else {
- if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
- addr = &nd->mac_addr;
-
- r = icmp6_send_router_solicitation(nd->fd, addr);
+ r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
if (r < 0)
log_ndisc(nd, "Error sending Router Solicitation");
else {