2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2015-10-16 18:09:10 +02:00
|
|
|
/***
|
2018-06-12 17:15:23 +02:00
|
|
|
Copyright © 2014 Intel Corporation. All rights reserved.
|
2015-10-16 18:09:10 +02:00
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
#include <netinet/in.h>
|
2015-10-25 13:14:12 +01:00
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2016-05-23 11:13:57 +02:00
|
|
|
#include <net/if.h>
|
2015-10-25 13:14:12 +01:00
|
|
|
#include <linux/if_packet.h>
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-16 18:09:10 +02:00
|
|
|
#include "icmp6-util.h"
|
2017-05-12 15:48:38 +02:00
|
|
|
#include "in-addr-util.h"
|
2018-11-26 21:52:36 +01:00
|
|
|
#include "io-util.h"
|
|
|
|
#include "socket-util.h"
|
2015-10-16 18:09:10 +02:00
|
|
|
|
|
|
|
#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
|
|
|
|
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
|
|
|
|
|
|
|
|
#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
|
|
|
|
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
|
|
|
|
|
2017-05-12 15:48:35 +02:00
|
|
|
static int icmp6_bind_router_message(const struct icmp6_filter *filter,
|
|
|
|
const struct ipv6_mreq *mreq) {
|
2019-03-18 12:01:02 +01:00
|
|
|
int ifindex = mreq->ipv6mr_interface;
|
2015-10-16 18:09:10 +02:00
|
|
|
_cleanup_close_ int s = -1;
|
network: beef up ipv6 RA support considerably
This reworks sd-ndisc and networkd substantially to support IPv6 RA much more
comprehensively. Since the API is extended quite a bit networkd has been ported
over too, and the patch is not as straight-forward as one could wish. The
rework includes:
- Support for DNSSL, RDNSS and RA routing options in sd-ndisc and networkd. Two
new configuration options have been added to networkd to make this
configurable.
- sd-ndisc now exposes an sd_ndisc_router object that encapsulates a full RA
message, and has direct, friendly acessor functions for the singleton RA
properties, as well as an iterative interface to iterate through known and
unsupported options. The router object may either be retrieved from the wire,
or generated from raw data. In many ways the sd-ndisc API now matches the
sd-lldp API, except that no implicit database of seen data is kept. (Note
that sd-ndisc actually had a half-written, but unused implementaiton of such
a store, which is removed now.)
- sd-ndisc will now collect the reception timestamps of RA, which is useful to
make sd_ndisc_router fully descriptive of what it covers.
Fixes: #1079
2016-06-02 20:38:12 +02:00
|
|
|
int r;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2015-11-23 15:59:58 +01:00
|
|
|
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
|
2015-10-16 18:09:10 +02:00
|
|
|
if (s < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2017-05-12 15:48:35 +02:00
|
|
|
r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter));
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq));
|
2015-10-16 18:09:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
/* RFC 3315, section 6.7, bullet point 2 may indicate that an
|
|
|
|
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 */
|
2019-03-18 12:01:02 +01:00
|
|
|
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex);
|
2015-10-16 18:09:10 +02:00
|
|
|
if (r < 0)
|
2018-10-18 22:39:55 +02:00
|
|
|
return r;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2018-10-18 19:48:18 +02:00
|
|
|
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false);
|
2015-10-16 18:09:10 +02:00
|
|
|
if (r < 0)
|
2018-10-18 19:48:18 +02:00
|
|
|
return r;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2018-10-18 22:39:55 +02:00
|
|
|
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
|
2015-10-16 18:09:10 +02:00
|
|
|
if (r < 0)
|
2018-10-18 22:39:55 +02:00
|
|
|
return r;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2018-10-18 22:39:55 +02:00
|
|
|
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
|
2015-11-23 15:59:58 +01:00
|
|
|
if (r < 0)
|
2018-10-18 22:39:55 +02:00
|
|
|
return r;
|
2015-11-23 15:59:58 +01:00
|
|
|
|
2018-10-18 19:48:18 +02:00
|
|
|
r = setsockopt_int(s, SOL_IPV6, IPV6_RECVHOPLIMIT, true);
|
2015-10-16 18:09:10 +02:00
|
|
|
if (r < 0)
|
2018-10-18 19:48:18 +02:00
|
|
|
return r;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
2018-10-18 19:48:18 +02:00
|
|
|
r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
|
network: beef up ipv6 RA support considerably
This reworks sd-ndisc and networkd substantially to support IPv6 RA much more
comprehensively. Since the API is extended quite a bit networkd has been ported
over too, and the patch is not as straight-forward as one could wish. The
rework includes:
- Support for DNSSL, RDNSS and RA routing options in sd-ndisc and networkd. Two
new configuration options have been added to networkd to make this
configurable.
- sd-ndisc now exposes an sd_ndisc_router object that encapsulates a full RA
message, and has direct, friendly acessor functions for the singleton RA
properties, as well as an iterative interface to iterate through known and
unsupported options. The router object may either be retrieved from the wire,
or generated from raw data. In many ways the sd-ndisc API now matches the
sd-lldp API, except that no implicit database of seen data is kept. (Note
that sd-ndisc actually had a half-written, but unused implementaiton of such
a store, which is removed now.)
- sd-ndisc will now collect the reception timestamps of RA, which is useful to
make sd_ndisc_router fully descriptive of what it covers.
Fixes: #1079
2016-06-02 20:38:12 +02:00
|
|
|
if (r < 0)
|
2018-10-18 19:48:18 +02:00
|
|
|
return r;
|
network: beef up ipv6 RA support considerably
This reworks sd-ndisc and networkd substantially to support IPv6 RA much more
comprehensively. Since the API is extended quite a bit networkd has been ported
over too, and the patch is not as straight-forward as one could wish. The
rework includes:
- Support for DNSSL, RDNSS and RA routing options in sd-ndisc and networkd. Two
new configuration options have been added to networkd to make this
configurable.
- sd-ndisc now exposes an sd_ndisc_router object that encapsulates a full RA
message, and has direct, friendly acessor functions for the singleton RA
properties, as well as an iterative interface to iterate through known and
unsupported options. The router object may either be retrieved from the wire,
or generated from raw data. In many ways the sd-ndisc API now matches the
sd-lldp API, except that no implicit database of seen data is kept. (Note
that sd-ndisc actually had a half-written, but unused implementaiton of such
a store, which is removed now.)
- sd-ndisc will now collect the reception timestamps of RA, which is useful to
make sd_ndisc_router fully descriptive of what it covers.
Fixes: #1079
2016-06-02 20:38:12 +02:00
|
|
|
|
2019-03-18 12:01:02 +01:00
|
|
|
r = socket_bind_to_ifindex(s, ifindex);
|
2016-05-23 11:13:57 +02:00
|
|
|
if (r < 0)
|
2019-03-18 12:01:02 +01:00
|
|
|
return r;
|
2016-05-23 11:13:57 +02:00
|
|
|
|
2018-03-22 17:04:29 +01:00
|
|
|
return TAKE_FD(s);
|
2015-10-16 18:09:10 +02:00
|
|
|
}
|
|
|
|
|
2020-07-02 08:49:19 +02:00
|
|
|
int icmp6_bind_router_solicitation(int ifindex) {
|
2017-05-12 15:48:35 +02:00
|
|
|
struct icmp6_filter filter = {};
|
|
|
|
struct ipv6_mreq mreq = {
|
|
|
|
.ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
|
2020-07-02 08:49:19 +02:00
|
|
|
.ipv6mr_interface = ifindex,
|
2017-05-12 15:48:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
|
|
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
|
|
|
|
|
|
|
return icmp6_bind_router_message(&filter, &mreq);
|
|
|
|
}
|
|
|
|
|
2020-07-02 08:49:19 +02:00
|
|
|
int icmp6_bind_router_advertisement(int ifindex) {
|
2017-05-12 15:48:35 +02:00
|
|
|
struct icmp6_filter filter = {};
|
|
|
|
struct ipv6_mreq mreq = {
|
|
|
|
.ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
|
2020-07-02 08:49:19 +02:00
|
|
|
.ipv6mr_interface = ifindex,
|
2017-05-12 15:48:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ICMP6_FILTER_SETBLOCKALL(&filter);
|
|
|
|
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
|
|
|
|
|
|
|
|
return icmp6_bind_router_message(&filter, &mreq);
|
|
|
|
}
|
|
|
|
|
2015-10-16 18:09:10 +02:00
|
|
|
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
|
|
|
struct sockaddr_in6 dst = {
|
|
|
|
.sin6_family = AF_INET6,
|
|
|
|
.sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
struct nd_router_solicit rs;
|
|
|
|
struct nd_opt_hdr rs_opt;
|
|
|
|
struct ether_addr rs_opt_mac;
|
|
|
|
} _packed_ rs = {
|
|
|
|
.rs.nd_rs_type = ND_ROUTER_SOLICIT,
|
2015-11-23 15:49:10 +01:00
|
|
|
.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
|
|
|
|
.rs_opt.nd_opt_len = 1,
|
2015-10-16 18:09:10 +02:00
|
|
|
};
|
2015-11-23 15:49:10 +01:00
|
|
|
struct iovec iov = {
|
|
|
|
.iov_base = &rs,
|
|
|
|
.iov_len = sizeof(rs),
|
2015-10-16 18:09:10 +02:00
|
|
|
};
|
|
|
|
struct msghdr msg = {
|
|
|
|
.msg_name = &dst,
|
|
|
|
.msg_namelen = sizeof(dst),
|
2015-11-23 15:49:10 +01:00
|
|
|
.msg_iov = &iov,
|
2015-10-16 18:09:10 +02:00
|
|
|
.msg_iovlen = 1,
|
|
|
|
};
|
|
|
|
int r;
|
|
|
|
|
2015-11-23 15:49:10 +01:00
|
|
|
assert(s >= 0);
|
|
|
|
assert(ether_addr);
|
|
|
|
|
|
|
|
rs.rs_opt_mac = *ether_addr;
|
2015-10-16 18:09:10 +02:00
|
|
|
|
|
|
|
r = sendmsg(s, &msg, 0);
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-12 15:48:38 +02:00
|
|
|
|
|
|
|
int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
|
|
|
|
triple_timestamp *timestamp) {
|
2020-04-24 23:54:25 +02:00
|
|
|
|
|
|
|
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
|
|
|
|
CMSG_SPACE(sizeof(struct timeval))) control;
|
2017-05-12 15:48:38 +02:00
|
|
|
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;
|
|
|
|
|
2018-11-26 21:52:36 +01:00
|
|
|
iov = IOVEC_MAKE(buffer, size);
|
2017-05-12 15:48:38 +02:00
|
|
|
|
2020-04-23 19:53:27 +02:00
|
|
|
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
|
2018-10-02 00:37:17 +02:00
|
|
|
if (len < 0)
|
2020-04-23 19:53:27 +02:00
|
|
|
return (int) len;
|
2017-05-12 15:48:38 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|