25#ifndef JOIN_FABRIC_NDP_HPP
26#define JOIN_FABRIC_NDP_HPP
38#include <system_error>
46#include <netinet/icmp6.h>
54 template <
class Protocol>
55 class BasicNdp :
public EventHandler
58 using Socket =
typename Protocol::Socket;
82 ,
_buffer (
std::make_unique<char[]> (Protocol::maxMsgSize))
88 if (
_socket.open (Protocol::Transport::v6 ()) == -1)
90 throw std::system_error (lastError,
"ndp socket open failed");
95 throw std::system_error (lastError,
"ndp socket bind to device failed");
100 if (::setsockopt (
_socket.handle (), IPPROTO_IPV6, IPV6_MULTICAST_IF, &
_index, sizeof (
_index)) == -1)
103 throw std::system_error (errno, std::generic_category (),
"ndp multicast setup failed");
107 if (::setsockopt (
_socket.handle (), IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof (on)) == -1)
110 throw std::system_error (errno, std::generic_category (),
"ndp hop limit setup failed");
114 if (::setsockopt (
_socket.handle (), IPPROTO_IPV6, IPV6_DONTFRAG, &on, sizeof (on)) == -1)
117 throw std::system_error (errno, std::generic_category (),
"ndp dontfrag setup failed");
121 ICMP6_FILTER_SETBLOCKALL (&
_filter);
125 throw std::system_error (lastError,
"ndp icmp6 filter setup failed");
130 struct ipv6_mreq mreq = {};
131 ::memcpy (&mreq.ipv6mr_multiaddr, group.
addr (), group.
length ());
132 mreq.ipv6mr_interface =
_index;
134 if (::setsockopt (
_socket.handle (), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1)
137 throw std::system_error (errno, std::generic_category (),
"ndp multicast membership setup failed");
182 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
186 lastError = std::make_error_code (std::errc::resource_deadlock_would_occur);
201 struct in6_addr group;
203 ::memcpy (&group.s6_addr[13],
static_cast<const uint8_t*
> (target.
addr ()) + 13, 3);
224 if (
send (payload,
static_cast<size_t> (size),
IpAddress (&group,
sizeof (group))) == -1)
267 return send (payload,
static_cast<size_t> (size), destination);
286 struct nd_neighbor_advert na;
287 ::memcpy (&na, payload,
sizeof (na));
289 if (solicitation.src.isWildcard ())
291 na.nd_na_flags_reserved &= ~ND_NA_FLAG_SOLICITED;
295 na.nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED;
298 ::memcpy (payload, &na,
sizeof (na));
300 return send (payload,
static_cast<size_t> (size),
411 unsigned int index () const noexcept
434 throw std::system_error (lastError,
"ndp reactor registration failed");
454 if (ICMP6_FILTER_WILLPASS (type, &
_filter) == pass)
459 struct icmp6_filter filter =
_filter;
463 ICMP6_FILTER_SETPASS (type, &filter);
467 ICMP6_FILTER_SETBLOCK (type, &filter);
470 if (::setsockopt (
_socket.handle (), IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof (filter)) == -1)
473 lastError = std::error_code (errno, std::generic_category ());
492 if (destination.
family () != AF_INET6)
498 if (size > Protocol::maxMsgSize)
508 return (
_socket.writeTo (data, size, endpoint) == -1) ? -1 : 0;
521 char control[CMSG_SPACE (
sizeof (
int))];
522 size_t controlSize =
sizeof (control);
524 ssize_t size =
_socket.readFrom (data, maxSize, &endpoint, control, &controlSize);
530 struct msghdr msg = {};
531 msg.msg_control = control;
532 msg.msg_controllen = controlSize;
536 for (
struct cmsghdr* cmsg = CMSG_FIRSTHDR (&msg); cmsg !=
nullptr; cmsg = CMSG_NXTHDR (&msg, cmsg))
538 if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_HOPLIMIT))
540 ::memcpy (&hop, CMSG_DATA (cmsg),
sizeof (hop));
544 if (hop != Protocol::hopLimit)
549 from = endpoint.ip ();
563 const uint8_t type =
static_cast<uint8_t
> (
_buffer[0]);
576 solicitation.
src = from;
581 listener (solicitation);
630 *pending->advert = advert;
631 pending->done =
true;
632 pending->cond.signal ();
652 throw std::system_error (errno, std::generic_category (),
"ndp interface lookup failed");
724 template <
class Protocol>
794 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
798 lastError = std::make_error_code (std::errc::resource_deadlock_would_occur);
802 RouterRequest pending;
803 pending.advert = &advert;
806 _pending.push_back (&pending);
810 _pending.erase (std::find (_pending.begin (), _pending.end (), &pending));
828 pending.cond.
timedWait (lock, timeout, [&pending] {
854 busy = _routerAdvertisementCalling || _routerAdvertisementListener;
857 _routerAdvertisementListener = std::move (cb);
882 _routerAdvertisementListener =
nullptr;
883 _routerAdvertisementCalling =
false;
894 void onReadable ([[maybe_unused]]
int fd)
override final
897 ssize_t size = this->
receive (this->
_buffer.get (), Protocol::maxMsgSize, from);
913 RouterAdvertisement advert;
914 if (this->
_message.
deserialize (advert, this->_buffer.get (),
static_cast<size_t> (size)) == -1)
921 if (_routerAdvertisementListener)
925 _routerAdvertisementCalling =
true;
928 if (_routerAdvertisementCalling)
930 _routerAdvertisementListener = std::move (listener);
931 _routerAdvertisementCalling =
false;
935 if (!_pending.empty ())
937 ScopedLock<Mutex> lock (_routerMutex);
939 for (RouterRequest* pending : _pending)
943 *pending->advert = advert;
944 pending->done =
true;
945 pending->cond.signal ();
960 RouterAdvertisement* advert =
nullptr;
967 std::vector<RouterRequest*> _pending;
977 bool _routerAdvertisementCalling =
false;
983 template <
class Protocol>
984 class BasicNdpServer final :
protected BasicNdp<Protocol>
1037 busy = _routerSolicitationCalling || _routerSolicitationListener;
1040 _routerSolicitationListener = std::move (cb);
1065 _routerSolicitationListener =
nullptr;
1066 _routerSolicitationCalling =
false;
1081 char payload[Protocol::maxMsgSize];
1083 advert.link.isWildcard () ?
hardware () : advert.link);
1089 return this->
send (payload,
static_cast<size_t> (size), destination);
1109 void onReadable ([[maybe_unused]]
int fd)
override final
1112 ssize_t size = this->
receive (this->
_buffer.get (), Protocol::maxMsgSize, from);
1123 RouterSolicitation solicitation;
1124 if (this->
_message.
deserialize (solicitation, this->_buffer.get (),
static_cast<size_t> (size)) == -1)
1129 if (from.
isWildcard () && !solicitation.link.isWildcard ())
1134 solicitation.src = from;
1136 if (_routerSolicitationListener)
1140 _routerSolicitationCalling =
true;
1141 listener (solicitation);
1143 if (_routerSolicitationCalling)
1145 _routerSolicitationListener = std::move (listener);
1146 _routerSolicitationCalling =
false;
1156 bool _routerSolicitationCalling =
false;
NDP client, solicits routers and receives their advertisements.
Definition ndp_protocol.hpp:37
int unsetRouterAdvertisementListener() noexcept
unset the router advertisement callback, it is no longer called once this returns.
Definition ndp.hpp:879
int routerSolicit(RouterAdvertisement &advert, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
send a router solicitation and wait for the first advertisement.
Definition ndp.hpp:793
Function< void(const RouterAdvertisement &advert)> RouterAdvertisementNotify
router advertisement notification callback.
Definition ndp.hpp:741
BasicNdpClient(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface and start receiving advertisements.
Definition ndp.hpp:754
int setRouterAdvertisementListener(RouterAdvertisementNotify cb) noexcept
set the callback called on every router advertisement received, solicited or not.
Definition ndp.hpp:849
~BasicNdpClient()
stop receiving advertisements and destroy the instance.
Definition ndp.hpp:763
int routerSolicit() noexcept
send a router solicitation.
Definition ndp.hpp:772
BasicNdpClient()=delete
create the BasicNdpClient instance.
int routerAdvertise(const RouterAdvertisement &advert, const IpAddress &destination=IpAddress::ipv6AllNodes) noexcept
send a router advertisement.
Definition ndp.hpp:1078
Function< void(const RouterSolicitation &solicitation)> RouterSolicitationNotify
router solicitation notification callback.
Definition ndp.hpp:1000
int routerAdvertise(const RouterAdvertisement &advert, const RouterSolicitation &solicitation) noexcept
answer a router solicitation, to the solicitor or to all the nodes of the link if it has no address.
Definition ndp.hpp:1098
BasicNdpServer(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface and start receiving solicitations.
Definition ndp.hpp:1013
~BasicNdpServer()
stop receiving solicitations and destroy the instance.
Definition ndp.hpp:1022
BasicNdpServer()=delete
create the BasicNdpServer instance.
int setRouterSolicitationListener(RouterSolicitationNotify cb) noexcept
set the callback called on every router solicitation received.
Definition ndp.hpp:1032
int unsetRouterSolicitationListener() noexcept
unset the router solicitation callback, it is no longer called once this returns.
Definition ndp.hpp:1062
carries NDP messages over an ICMPv6 socket.
Definition ndp_protocol.hpp:34
void detach() noexcept
unregister from the reactor, to be called by the final class before destruction starts.
Definition ndp.hpp:441
const std::string _interface
interface name.
Definition ndp.hpp:668
Mutex _neighborMutex
neighbor solicitations protection mutex.
Definition ndp.hpp:704
typename Protocol::Endpoint Endpoint
Definition ndp.hpp:59
NeighborAdvertisementNotify _neighborAdvertisementListener
neighbor advertisement listener, only accessed from the reactor thread.
Definition ndp.hpp:714
ssize_t receive(char *data, size_t maxSize, IpAddress &from)
read a message that passed the checks common to every NDP message.
Definition ndp.hpp:518
BasicNdp(const std::string &interface, uint8_t filter, const IpAddress &group, Reactor &reactor)
create the instance bound to the given interface.
Definition ndp.hpp:80
std::vector< NeighborRequest * > _neighborPending
neighbor solicitations waiting for an advertisement, only accessed from the reactor thread.
Definition ndp.hpp:701
bool _neighborAdvertisementCalling
Definition ndp.hpp:718
Function< void(const NeighborSolicitation &solicitation)> NeighborSolicitationNotify
neighbor solicitation notification callback.
Definition ndp.hpp:62
static unsigned int lookup(const std::string &interface)
get the index of the given interface.
Definition ndp.hpp:647
int neighborSolicit(const IpAddress &target, NeighborAdvertisement &advert, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
send a neighbor solicitation and wait for the advertisement of the target.
Definition ndp.hpp:181
Socket _socket
underlying socket.
Definition ndp.hpp:659
NdpMessage _message
NDP message codec.
Definition ndp.hpp:662
unsigned int index() const noexcept
get the index of the interface the instance is bound to.
Definition ndp.hpp:411
BasicNdp(const BasicNdp &other)=delete
create instance by copy.
const MacAddress _hardware
hardware address of the interface.
Definition ndp.hpp:674
BasicNdp(BasicNdp &&other)=delete
create instance by move.
bool _neighborSolicitationCalling
Definition ndp.hpp:711
int applyFilter(uint8_t type, bool pass) noexcept
pass or block a message type in the ICMPv6 filter.
Definition ndp.hpp:452
int unsetNeighborSolicitationListener() noexcept
unset the neighbor solicitation callback, it is no longer called once this returns.
Definition ndp.hpp:340
bool onNeighborMessage(size_t size, const IpAddress &from) noexcept
hand a received neighbor message to its listener, and an advertisement to the solicitations waiting f...
Definition ndp.hpp:561
int send(const char *data, size_t size, const IpAddress &destination)
write a message on the interface.
Definition ndp.hpp:490
Reactor & _reactor
event loop reactor.
Definition ndp.hpp:677
virtual ~BasicNdp()=default
destroy the instance.
int unsetNeighborAdvertisementListener() noexcept
unset the neighbor advertisement callback, it is no longer called once this returns.
Definition ndp.hpp:387
int neighborAdvertise(const NeighborAdvertisement &advert, const IpAddress &destination=IpAddress::ipv6AllNodes) noexcept
send a neighbor advertisement, the interface hardware address is used if it carries none.
Definition ndp.hpp:256
int setNeighborAdvertisementListener(NeighborAdvertisementNotify cb) noexcept
set the callback called on every neighbor advertisement received, solicited or not.
Definition ndp.hpp:356
void attach()
register with the reactor, to be called by the final class once fully constructed.
Definition ndp.hpp:430
BasicNdp & operator=(const BasicNdp &other)=delete
assign instance by copy.
const unsigned int _index
interface index.
Definition ndp.hpp:671
typename Protocol::Socket Socket
Definition ndp.hpp:58
struct icmp6_filter _filter
ICMPv6 filter applied to the socket, only accessed from the reactor thread once constructed.
Definition ndp.hpp:680
int setNeighborSolicitationListener(NeighborSolicitationNotify cb) noexcept
set the callback called on every neighbor solicitation received.
Definition ndp.hpp:309
NeighborSolicitationNotify _neighborSolicitationListener
neighbor solicitation listener, only accessed from the reactor thread.
Definition ndp.hpp:707
BasicNdp()=delete
create the BasicNdp instance.
std::unique_ptr< char[]> _buffer
receive buffer.
Definition ndp.hpp:665
const MacAddress & hardware() const noexcept
get the hardware address of the interface the instance is bound to.
Definition ndp.hpp:420
const std::string & interface() const noexcept
get the name of the interface the instance is bound to.
Definition ndp.hpp:402
int neighborAdvertise(const NeighborAdvertisement &advert, const NeighborSolicitation &solicitation) noexcept
answer a neighbor solicitation, to the solicitor or to all the nodes of the link if it has no address...
Definition ndp.hpp:276
Function< void(const NeighborAdvertisement &advert)> NeighborAdvertisementNotify
neighbor advertisement notification callback.
Definition ndp.hpp:65
condition variable class.
Definition condition.hpp:42
bool timedWait(Lock &lock, std::chrono::duration< Rep, Period > timeout)
wait on a condition until timeout expire.
Definition condition.hpp:121
IPv6, IPv4 address class.
Definition ip_address.hpp:52
bool isLinkLocal() const
check if IP address is link local.
Definition ip_address.cpp:1313
static const IpAddress ipv6Routers
routers multicast IPv6 address.
Definition ip_address.hpp:364
static const IpAddress ipv6AllNodes
all nodes multicast IPv6 address.
Definition ip_address.hpp:358
static const IpAddress ipv6SolicitedNodes
solicited nodes multicast IPv6 address.
Definition ip_address.hpp:361
int family() const
get address family.
Definition ip_address.cpp:1250
socklen_t length() const
get the size in byte of the internal address structure.
Definition ip_address.cpp:1268
bool isWildcard() const
check if IP address is a wildcard address.
Definition ip_address.cpp:1295
const void * addr() const
get the internal address structure.
Definition ip_address.cpp:1259
MAC address class.
Definition mac_address.hpp:47
bool isWildcard() const
check if MAC address is a wildcard address.
Definition mac_address.cpp:182
class used to protect shared data from being simultaneously accessed by multiple threads.
Definition mutex.hpp:37
NDP message codec.
Definition ndp_message.hpp:163
static constexpr size_t maxNeighborAdvertisementSize
biggest neighbor advertisement the codec writes, link layer address option included.
Definition ndp_message.hpp:702
static constexpr size_t maxRouterSolicitationSize
biggest router solicitation the codec writes, link layer address option included.
Definition ndp_message.hpp:694
ssize_t serialize(const RouterSolicitation &packet, char *data, size_t maxSize) const
serialize a router solicitation into a buffer.
Definition ndp_message.hpp:195
@ NeighborAdvert
Definition ndp_message.hpp:173
@ NeighborSolicit
Definition ndp_message.hpp:172
static constexpr size_t maxNeighborSolicitationSize
biggest neighbor solicitation the codec writes, link layer address option included.
Definition ndp_message.hpp:698
int deserialize(RouterSolicitation &packet, const char *data, size_t size) const
deserialize a router solicitation from a buffer.
Definition ndp_message.hpp:398
static Reactor & reactor()
get the global Reactor instance.
Definition reactor.cpp:612
Reactor class.
Definition reactor.hpp:159
bool isReactorThread() const noexcept
check if the calling thread is the reactor thread.
Definition reactor.cpp:337
int delHandler(int fd, bool sync=true) noexcept
delete handler from reactor.
Definition reactor.cpp:156
int addHandler(int fd, EventHandler *handler, bool wantRead=true, bool wantWrite=false, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:101
int invoke(InvokeHandler *fn, bool sync=true) noexcept
invoke a function from the reactor thread.
Definition reactor.cpp:199
class owning a mutex for the duration of a scoped block.
Definition mutex.hpp:249
Definition acceptor.hpp:32
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
neighbor solicitation waiting for the advertisement of its target.
Definition ndp.hpp:686
Condition cond
answer notification.
Definition ndp.hpp:688
bool done
set once the advertisement has been received.
Definition ndp.hpp:697
NeighborAdvertisement * advert
receives the advertisement.
Definition ndp.hpp:694
IpAddress target
address of the solicited neighbor.
Definition ndp.hpp:691
neighbor advertisement, RFC 4861 section 4.4.
Definition ndp_message.hpp:145
IpAddress src
address the advertisement came from, set by the transport.
Definition ndp_message.hpp:147
IpAddress target
address the advertisement is about.
Definition ndp_message.hpp:153
MacAddress link
link layer address of the target, wildcard if not carried.
Definition ndp_message.hpp:156
neighbor solicitation, RFC 4861 section 4.3.
Definition ndp_message.hpp:130
MacAddress link
link layer address of the sender, wildcard if not carried.
Definition ndp_message.hpp:138
IpAddress src
address the solicitation came from, set by the transport.
Definition ndp_message.hpp:132
IpAddress target
address of the solicited neighbor.
Definition ndp_message.hpp:135
router advertisement, RFC 4861 section 4.2.
Definition ndp_message.hpp:94
IpAddress src
address the advertisement came from, set by the transport.
Definition ndp_message.hpp:96
router solicitation, RFC 4861 section 4.1.
Definition ndp_message.hpp:82
MacAddress link
link layer address of the sender, wildcard if not carried.
Definition ndp_message.hpp:87
IpAddress address
Definition tcp_acceptor_test.cpp:35