summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/icmp6-util.c
diff options
context:
space:
mode:
authorPatrik Flykt <patrik.flykt@linux.intel.com>2017-05-12 15:48:38 +0200
committerPatrik Flykt <patrik.flykt@linux.intel.com>2017-05-15 13:49:50 +0200
commit88d5a3db555af62dcde306e9de88ecfc73db80ab (patch)
tree14c65af43a8eb109cfd2f6736b3561918a4947aa /src/libsystemd-network/icmp6-util.c
parentsd-radv: Send Router Advertisments (diff)
downloadsystemd-88d5a3db555af62dcde306e9de88ecfc73db80ab.tar.xz
systemd-88d5a3db555af62dcde306e9de88ecfc73db80ab.zip
sd-radv: Receive Router Solicitations
Receive Router Solicitations and send a unicast Router Advertisment in response. Refactor ICMPv6 packet handling code so that the common ICMPv6 validation parts are reused between the existing router discovery and the new functionality adding reception of Router Solicitation messages.
Diffstat (limited to 'src/libsystemd-network/icmp6-util.c')
-rw-r--r--src/libsystemd-network/icmp6-util.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
index f1cb0bc8a0..7fbebd6f27 100644
--- a/src/libsystemd-network/icmp6-util.c
+++ b/src/libsystemd-network/icmp6-util.c
@@ -32,6 +32,7 @@
#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, \
@@ -164,3 +165,74 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
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;
+}