face: implement IPv6 UDP multicast transport
Change-Id: Ib6ab956354dbbba00694c7949fa9ee4639579879
Refs: #4222
diff --git a/daemon/face/multicast-udp-transport.cpp b/daemon/face/multicast-udp-transport.cpp
index 86324d3..8912759 100644
--- a/daemon/face/multicast-udp-transport.cpp
+++ b/daemon/face/multicast-udp-transport.cpp
@@ -27,6 +27,8 @@
#include "socket-utils.hpp"
#include "udp-protocol.hpp"
+#include <boost/functional/hash.hpp>
+
#ifdef __linux__
#include <cerrno> // for errno
#include <cstring> // for std::strerror()
@@ -39,8 +41,7 @@
NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramTransport, MulticastUdpTransport::protocol,
Multicast, "MulticastUdpTransport");
-MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndpoint,
- const protocol::endpoint& multicastGroup,
+MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& multicastGroup,
protocol::socket&& recvSocket,
protocol::socket&& sendSocket,
ndn::nfd::LinkType linkType)
@@ -48,12 +49,12 @@
, m_multicastGroup(multicastGroup)
, m_sendSocket(std::move(sendSocket))
{
- this->setLocalUri(FaceUri(localEndpoint));
+ this->setLocalUri(FaceUri(m_sendSocket.local_endpoint()));
this->setRemoteUri(FaceUri(multicastGroup));
this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
this->setLinkType(linkType);
- this->setMtu(udp::computeMtu(localEndpoint));
+ this->setMtu(udp::computeMtu(m_sendSocket.local_endpoint()));
protocol::socket::send_buffer_size sendBufferSizeOption;
boost::system::error_code error;
@@ -117,16 +118,25 @@
sock.open(multicastGroup.protocol());
sock.set_option(protocol::socket::reuse_address(true));
- sock.bind(protocol::endpoint(multicastGroup.protocol(), multicastGroup.port()));
if (multicastGroup.address().is_v4()) {
BOOST_ASSERT(localAddress.is_v4());
+ sock.bind(multicastGroup);
sock.set_option(boost::asio::ip::multicast::join_group(multicastGroup.address().to_v4(),
localAddress.to_v4()));
}
else {
- // IPv6 multicast is not supported
- BOOST_ASSERT(false);
+ BOOST_ASSERT(localAddress.is_v6());
+ sock.set_option(boost::asio::ip::v6_only(true));
+#ifdef WITH_TESTS
+ // To simplify unit tests, we bind to the "any" IPv6 address if the supplied multicast
+ // address lacks a scope id. Calling bind() without a scope id would otherwise fail.
+ if (multicastGroup.address().to_v6().scope_id() == 0)
+ sock.bind(protocol::endpoint(boost::asio::ip::address_v6::any(), multicastGroup.port()));
+ else
+#endif
+ sock.bind(multicastGroup);
+ sock.set_option(boost::asio::ip::multicast::join_group(multicastGroup.address().to_v6()));
}
#ifdef __linux__
@@ -149,6 +159,7 @@
void
MulticastUdpTransport::openTxSocket(protocol::socket& sock,
const protocol::endpoint& localEndpoint,
+ const shared_ptr<const ndn::net::NetworkInterface>& netif,
bool enableLoopback)
{
BOOST_ASSERT(!sock.is_open());
@@ -156,15 +167,17 @@
sock.open(localEndpoint.protocol());
sock.set_option(protocol::socket::reuse_address(true));
sock.set_option(boost::asio::ip::multicast::enable_loopback(enableLoopback));
- sock.bind(localEndpoint);
if (localEndpoint.address().is_v4()) {
+ sock.bind(localEndpoint);
if (!localEndpoint.address().is_unspecified())
sock.set_option(boost::asio::ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
}
else {
- // IPv6 multicast is not supported
- BOOST_ASSERT(false);
+ sock.set_option(boost::asio::ip::v6_only(true));
+ sock.bind(localEndpoint);
+ if (netif)
+ sock.set_option(boost::asio::ip::multicast::outbound_interface(netif->getIndex()));
}
}
@@ -172,11 +185,17 @@
Transport::EndpointId
DatagramTransport<boost::asio::ip::udp, Multicast>::makeEndpointId(const protocol::endpoint& ep)
{
- // IPv6 multicast is not supported
- BOOST_ASSERT(ep.address().is_v4());
-
- return (static_cast<uint64_t>(ep.port()) << 32) |
- static_cast<uint64_t>(ep.address().to_v4().to_ulong());
+ if (ep.address().is_v4()) {
+ return (static_cast<uint64_t>(ep.port()) << 32) |
+ static_cast<uint64_t>(ep.address().to_v4().to_ulong());
+ }
+ else {
+ size_t seed = 0;
+ const auto& addrBytes = ep.address().to_v6().to_bytes();
+ boost::hash_range(seed, addrBytes.begin(), addrBytes.end());
+ boost::hash_combine(seed, ep.port());
+ return seed;
+ }
}
} // namespace face