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 {