#include "fd-util.h"
#include "icmp6-util.h"
#include "socket-util.h"
+#include "in-addr-util.h"
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
return 0;
}
+
+int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
+ triple_timestamp *timestamp) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
+ CMSG_SPACE(sizeof(struct timeval))];
+ } 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;
+ ssize_t len;
+
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return -errno;
+ }
+
+ if ((size_t) len != size)
+ return -EINVAL;
+
+ if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
+ sa.in6.sin6_family == AF_INET6) {
+
+ *dst = sa.in6.sin6_addr;
+ if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) dst) <= 0)
+ return -EADDRNOTAVAIL;
+
+ } else if (msg.msg_namelen > 0)
+ return -EPFNOSUPPORT;
+
+ /* namelen == 0 only happens when running the test-suite over a socketpair */
+
+ 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)
+ return -EMULTIHOP;
+ }
+
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+ triple_timestamp_from_realtime(timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+ }
+
+ if (!triple_timestamp_is_set(timestamp))
+ triple_timestamp_get(timestamp);
+
+ return 0;
+}
#include <net/ethernet.h>
+#include "time-util.h"
+
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
int icmp6_bind_router_solicitation(int index);
int icmp6_bind_router_advertisement(int index);
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
+ triple_timestamp *timestamp);
int fd;
unsigned ra_sent;
+ sd_event_source *recv_event_source;
sd_event_source *timeout_event_source;
unsigned n_prefixes;
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
sd_ndisc *nd = userdata;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
- CMSG_SPACE(sizeof(struct timeval))];
- } 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;
- ssize_t len, buflen;
+ ssize_t buflen;
+ int r;
+ _cleanup_free_ char *addr = NULL;
assert(s);
assert(nd);
if (!rt)
return -ENOMEM;
- iov.iov_base = NDISC_ROUTER_RAW(rt);
- iov.iov_len = rt->raw_size;
-
- len = recvmsg(fd, &msg, MSG_DONTWAIT);
- if (len < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
- }
-
- if ((size_t) len != rt->raw_size) {
- log_ndisc("Packet size mismatch.");
- return -EINVAL;
- }
-
- if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
- sa.in6.sin6_family == AF_INET6) {
-
- if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) {
- _cleanup_free_ char *addr = NULL;
-
- (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr);
- log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr));
- return 0;
- }
-
- rt->address = sa.in6.sin6_addr;
-
- } else if (msg.msg_namelen > 0) {
- log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen);
- return -EINVAL;
- }
-
- /* namelen == 0 only happens when running the test-suite over a socketpair */
-
- 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("Received RA with invalid hop limit %d. Ignoring.", hops);
- return 0;
- }
+ r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address,
+ &rt->timestamp);
+ if (r < 0) {
+ switch (r) {
+ case -EADDRNOTAVAIL:
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &rt->address, &addr);
+ log_ndisc("Received RA from non-link-local address %s. Ignoring", addr);
+ break;
+
+ case -EMULTIHOP:
+ log_ndisc("Received RA with invalid hop limit. Ignoring.");
+ break;
+
+ case -EPFNOSUPPORT:
+ log_ndisc("Received invalid source address from ICMPv6 socket.");
+ break;
}
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SO_TIMESTAMP &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
- triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+ return 0;
}
- if (!triple_timestamp_is_set(&rt->timestamp))
- triple_timestamp_get(&rt->timestamp);
-
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
return ndisc_handle_datagram(nd, rt);
ra->timeout_event_source =
sd_event_source_unref(ra->timeout_event_source);
+ ra->recv_event_source =
+ sd_event_source_unref(ra->recv_event_source);
+
ra->ra_sent = 0;
}
.msg_iov = iov,
};
- if (dst)
+ if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
dst_addr.sin6_addr = *dst;
+
adv.nd_ra_type = ND_ROUTER_ADVERT;
adv.nd_ra_curhoplimit = ra->hop_limit;
adv.nd_ra_flags_reserved = ra->flags;
return 0;
}
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_radv *ra = userdata;
+ _cleanup_free_ char *addr = NULL;
+ struct in6_addr src;
+ triple_timestamp timestamp;
+ int r;
+ ssize_t buflen;
+ _cleanup_free_ char *buf = NULL;
+
+ assert(s);
+ assert(ra);
+ assert(ra->event);
+
+ buflen = next_datagram_size_fd(fd);
+
+ if ((unsigned) buflen < sizeof(struct nd_router_solicit))
+ return log_radv("Too short packet received");
+
+ buf = new0(char, buflen);
+ if (!buf)
+ return 0;
+
+ r = icmp6_receive(fd, buf, buflen, &src, ×tamp);
+ if (r < 0) {
+ switch (r) {
+ case -EADDRNOTAVAIL:
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
+ log_radv("Received RS from non-link-local address %s. Ignoring", addr);
+ break;
+
+ case -EMULTIHOP:
+ log_radv("Received RS with invalid hop limit. Ignoring.");
+ break;
+
+ case -EPFNOSUPPORT:
+ log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
+ break;
+
+ default:
+ log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m");
+ break;
+ }
+
+ return 0;
+ }
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
+
+ r = radv_send(ra, &src, ra->lifetime);
+ if (r < 0)
+ log_radv_warning_errno(r, "Unable to send solicited Router Advertisment to %s: %m", addr);
+ else
+ log_radv("Sent solicited Router Advertisement to %s", addr);
+
+ return 0;
+}
+
static usec_t radv_compute_timeout(usec_t min, usec_t max) {
assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC);
goto fail;
ra->fd = r;
- r = 0;
+
+ r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
+ if (r < 0)
+ goto fail;
+
+ (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
ra->state = SD_RADV_STATE_ADVERTISING;
return -ENOSYS;
}
+int icmp6_receive(int fd, void *iov_base, size_t iov_len,
+ struct in6_addr *dst, triple_timestamp *timestamp) {
+ assert (read (fd, iov_base, iov_len) == (ssize_t)iov_len);
+
+ if (timestamp)
+ triple_timestamp_get(timestamp);
+
+ return 0;
+}
+
static int send_ra(uint8_t flags) {
uint8_t advertisement[] = {
0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,