diff --git a/.jenkins.d/20-tests.sh b/.jenkins.d/20-tests.sh index 6f227005..b52304d4 100755 --- a/.jenkins.d/20-tests.sh +++ b/.jenkins.d/20-tests.sh @@ -49,6 +49,6 @@ export ASAN_OPTIONS # Then use sudo to run those tests that need superuser powers sudo ./build/unit-tests-core -t TestPrivilegeHelper $(ut_log_args core-privilege) -sudo ./build/unit-tests-daemon -t Face/TestEthernetFactory,TestEthernetTransport $(ut_log_args daemon-ethernet) -sudo ./build/unit-tests-daemon -t Face/TestFaceSystem/Config* $(ut_log_args daemon-face-config) +sudo ./build/unit-tests-daemon -t Face/TestEthernetTransport $(ut_log_args daemon-ethernet) +sudo ./build/unit-tests-daemon -t Face/*Factory/ProcessConfig $(ut_log_args daemon-face-config) sudo ./build/unit-tests-daemon -t Mgmt/TestGeneralConfigSection/UserAndGroupConfig,NoUserConfig $(ut_log_args daemon-user-config) diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp index e5b51753..5359e2d2 100644 --- a/daemon/face/face-system.cpp +++ b/daemon/face/face-system.cpp @@ -24,7 +24,7 @@ */ #include "face-system.hpp" -#include "core/logger.hpp" +// #include "core/logger.hpp" #include "fw/face-table.hpp" // ProtocolFactory includes, sorted alphabetically @@ -43,7 +43,7 @@ namespace nfd { namespace face { -NFD_LOG_INIT("FaceSystem"); +// NFD_LOG_INIT("FaceSystem"); FaceSystem::FaceSystem(FaceTable& faceTable) : m_faceTable(faceTable) @@ -56,6 +56,8 @@ FaceSystem::FaceSystem(FaceTable& faceTable) m_factories["tcp"] = make_shared(); + m_factories["udp"] = make_shared(); + #ifdef HAVE_UNIX_SOCKETS m_factories["unix"] = make_shared(); #endif // HAVE_UNIX_SOCKETS @@ -126,7 +128,7 @@ FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, con std::set seenSections; for (const auto& pair : configSection) { const std::string& sectionName = pair.first; - const ConfigSection& subSection = pair.second; + // const ConfigSection& subSection = pair.second; if (!seenSections.insert(sectionName).second) { BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName)); @@ -138,168 +140,7 @@ FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, con ///\todo #3521 nicfaces - ///\todo #3904 process these in protocol factory - if (sectionName == "udp") { - processSectionUdp(subSection, isDryRun, context.m_netifs); - } - else { - BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName)); - } - } -} - -void -FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun, - const std::vector& nicList) -{ - // ; the udp section contains settings of UDP faces and channels - // udp - // { - // port 6363 ; UDP unicast port number - // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face - // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes - - // ; NFD creates one UDP multicast face per NIC - // mcast yes ; set to 'no' to disable UDP multicast, default 'yes' - // mcast_port 56363 ; UDP multicast port number - // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only) - // } - - uint16_t port = 6363; - bool enableV4 = true; - bool enableV6 = true; - size_t timeout = 600; - size_t keepAliveInterval = 25; - bool useMcast = true; - auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170"); - uint16_t mcastPort = 56363; - - for (const auto& i : configSection) { - if (i.first == "port") { - port = ConfigFile::parseNumber(i, "udp"); - NFD_LOG_TRACE("UDP unicast port set to " << port); - } - else if (i.first == "enable_v4") { - enableV4 = ConfigFile::parseYesNo(i, "udp"); - } - else if (i.first == "enable_v6") { - enableV6 = ConfigFile::parseYesNo(i, "udp"); - } - else if (i.first == "idle_timeout") { - try { - timeout = i.second.get_value(); - } - catch (const boost::property_tree::ptree_bad_data&) { - BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" + - i.first + "\" in \"udp\" section")); - } - } - else if (i.first == "keep_alive_interval") { - try { - keepAliveInterval = i.second.get_value(); - /// \todo Make use of keepAliveInterval - (void)(keepAliveInterval); - } - catch (const boost::property_tree::ptree_bad_data&) { - BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" + - i.first + "\" in \"udp\" section")); - } - } - else if (i.first == "mcast") { - useMcast = ConfigFile::parseYesNo(i, "udp"); - } - else if (i.first == "mcast_port") { - mcastPort = ConfigFile::parseNumber(i, "udp"); - NFD_LOG_TRACE("UDP multicast port set to " << mcastPort); - } - else if (i.first == "mcast_group") { - boost::system::error_code ec; - mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value(), ec); - if (ec) { - BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" + - i.first + "\" in \"udp\" section")); - } - NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup); - } - else { - BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + - i.first + "\" in \"udp\" section")); - } - } - - if (!enableV4 && !enableV6) { - BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled." - " Remove \"udp\" section to disable UDP channels or" - " re-enable at least one channel type.")); - } - else if (useMcast && !enableV4) { - BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels" - " have been disabled (conflicting configuration options set)")); - } - - if (!isDryRun) { - shared_ptr factory; - bool isReload = false; - if (m_factoryByScheme.count("udp") > 0) { - isReload = true; - factory = static_pointer_cast(m_factoryByScheme["udp"]); - } - else { - factory = make_shared(); - m_factoryByScheme.emplace("udp", factory); - } - - if (!isReload && enableV4) { - udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port); - shared_ptr v4Channel = factory->createChannel(endpoint, time::seconds(timeout)); - v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr); - - m_factoryByScheme.emplace("udp4", factory); - } - - if (!isReload && enableV6) { - udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port); - shared_ptr v6Channel = factory->createChannel(endpoint, time::seconds(timeout)); - v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr); - - m_factoryByScheme.emplace("udp6", factory); - } - - std::set> multicastFacesToRemove; - for (const auto& i : factory->getMulticastFaces()) { - multicastFacesToRemove.insert(i.second); - } - - if (useMcast && enableV4) { - std::vector ipv4MulticastInterfaces; - for (const auto& nic : nicList) { - if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) { - ipv4MulticastInterfaces.push_back(nic); - } - } - - bool isNicNameNecessary = false; -#if defined(__linux__) - if (ipv4MulticastInterfaces.size() > 1) { - // On Linux if we have more than one MulticastUdpFace - // we need to specify the name of the interface - isNicNameNecessary = true; - } -#endif - - udp::Endpoint mcastEndpoint(mcastGroup, mcastPort); - for (const auto& nic : ipv4MulticastInterfaces) { - udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort); - auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint, - isNicNameNecessary ? nic.name : ""); - m_faceTable.add(newFace); - multicastFacesToRemove.erase(newFace); - } - } - - for (const auto& face : multicastFacesToRemove) { - face->close(); - } + BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName)); } } diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp index 4d374c74..f6a7c716 100644 --- a/daemon/face/face-system.hpp +++ b/daemon/face/face-system.hpp @@ -98,10 +98,6 @@ private: processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename); - void - processSectionUdp(const ConfigSection& configSection, bool isDryRun, - const std::vector& nicList); - PUBLIC_WITH_TESTS_ELSE_PRIVATE: /** \brief config section name => protocol factory * diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp index a2506d4b..e86e8e9b 100644 --- a/daemon/face/udp-channel.hpp +++ b/daemon/face/udp-channel.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (c) 2014-2015, Regents of the University of California, + * Copyright (c) 2014-2017, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -27,13 +27,10 @@ #define NFD_DAEMON_FACE_UDP_CHANNEL_HPP #include "channel.hpp" +#include "udp-protocol.hpp" namespace nfd { -namespace udp { -typedef boost::asio::ip::udp::endpoint Endpoint; -} // namespace udp - /** * \brief Class implementing UDP-based channel to create faces */ diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp index 88ac221f..c9b4ac4d 100644 --- a/daemon/face/udp-factory.cpp +++ b/daemon/face/udp-factory.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (c) 2014-2016, Regents of the University of California, + * Copyright (c) 2014-2017, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -27,20 +27,198 @@ #include "generic-link-service.hpp" #include "multicast-udp-transport.hpp" #include "core/global-io.hpp" -#include "core/network-interface.hpp" +#include +#include #ifdef __linux__ #include // for errno #include // for std::strerror() #include // for setsockopt() -#endif +#endif // __linux__ namespace nfd { +namespace face { namespace ip = boost::asio::ip; NFD_LOG_INIT("UdpFactory"); +void +UdpFactory::processConfig(OptionalConfigSection configSection, + FaceSystem::ConfigContext& context) +{ + // udp + // { + // port 6363 + // enable_v4 yes + // enable_v6 yes + // idle_timeout 600 + // keep_alive_interval 25 ; acceptable but ignored + // mcast yes + // mcast_group 224.0.23.170 + // mcast_port 56363 + // } + + uint16_t port = 6363; + bool enableV4 = false; + bool enableV6 = false; + uint32_t idleTimeout = 600; + MulticastConfig mcastConfig; + + if (configSection) { + // These default to 'yes' but only if face_system.udp section is present + enableV4 = enableV6 = mcastConfig.isEnabled = true; + + for (const auto& pair : *configSection) { + const std::string& key = pair.first; + const ConfigSection& value = pair.second; + + if (key == "port") { + port = ConfigFile::parseNumber(pair, "face_system.udp"); + } + else if (key == "enable_v4") { + enableV4 = ConfigFile::parseYesNo(pair, "face_system.udp"); + } + else if (key == "enable_v6") { + enableV6 = ConfigFile::parseYesNo(pair, "face_system.udp"); + } + else if (key == "idle_timeout") { + idleTimeout = ConfigFile::parseNumber(pair, "face_system.udp"); + } + else if (key == "keep_alive_interval") { + // ignored + } + else if (key == "mcast") { + mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.udp"); + } + else if (key == "mcast_group") { + const std::string& valueStr = value.get_value(); + boost::system::error_code ec; + mcastConfig.group.address(boost::asio::ip::address_v4::from_string(valueStr, ec)); + if (ec) { + BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" + + valueStr + "' cannot be parsed as an IPv4 address")); + } + else if (!mcastConfig.group.address().is_multicast()) { + BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" + + valueStr + "' is not a multicast address")); + } + } + else if (key == "mcast_port") { + mcastConfig.group.port(ConfigFile::parseNumber(pair, "face_system.udp")); + } + else { + BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.udp." + key)); + } + } + + if (!enableV4 && !enableV6 && !mcastConfig.isEnabled) { + BOOST_THROW_EXCEPTION(ConfigFile::Error( + "IPv4 and IPv6 UDP channels and UDP multicast have been disabled. " + "Remove face_system.udp section to disable UDP channels or enable at least one of them.")); + } + } + + if (!context.isDryRun) { + if (enableV4) { + udp::Endpoint endpoint(ip::udp::v4(), port); + shared_ptr v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout)); + if (!v4Channel->isListening()) { + v4Channel->listen(context.addFace, nullptr); + } + providedSchemes.insert("udp"); + providedSchemes.insert("udp4"); + } + else if (providedSchemes.count("udp4") > 0) { + NFD_LOG_WARN("Cannot close udp4 channel after its creation"); + } + + if (enableV6) { + udp::Endpoint endpoint(ip::udp::v6(), port); + shared_ptr v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout)); + if (!v6Channel->isListening()) { + v6Channel->listen(context.addFace, nullptr); + } + providedSchemes.insert("udp"); + providedSchemes.insert("udp6"); + } + else if (providedSchemes.count("udp6") > 0) { + NFD_LOG_WARN("Cannot close udp6 channel after its creation"); + } + + if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) { + if (mcastConfig.isEnabled) { + NFD_LOG_INFO("enabling multicast on " << mcastConfig.group); + } + else { + NFD_LOG_INFO("disabling multicast"); + } + } + else if (m_mcastConfig.group != mcastConfig.group) { + NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group << + " to " << mcastConfig.group); + } + else { + // There's no configuration change, but we still need to re-apply configuration because + // netifs may have changed. + } + + m_mcastConfig = mcastConfig; + this->applyMulticastConfig(context); + } +} + +void +UdpFactory::createFace(const FaceUri& uri, + ndn::nfd::FacePersistency persistency, + bool wantLocalFieldsEnabled, + const FaceCreatedCallback& onCreated, + const FaceCreationFailedCallback& onFailure) +{ + BOOST_ASSERT(uri.isCanonical()); + + if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) { + NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND"); + onFailure(406, "Outgoing unicast UDP faces do not support on-demand persistency"); + return; + } + + udp::Endpoint endpoint(ip::address::from_string(uri.getHost()), + boost::lexical_cast(uri.getPort())); + + if (endpoint.address().is_multicast()) { + NFD_LOG_TRACE("createFace does not support multicast faces"); + onFailure(406, "Cannot create multicast UDP faces"); + return; + } + + if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) { + NFD_LOG_TRACE("Requested endpoint is prohibited " + "(reserved by this NFD or disallowed by face management protocol)"); + onFailure(406, "Requested endpoint is prohibited"); + return; + } + + if (wantLocalFieldsEnabled) { + // UDP faces are never local + NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled"); + onFailure(406, "Local fields can only be enabled on faces with local scope"); + return; + } + + // very simple logic for now + for (const auto& i : m_channels) { + if ((i.first.address().is_v4() && endpoint.address().is_v4()) || + (i.first.address().is_v6() && endpoint.address().is_v6())) { + i.second->connect(endpoint, persistency, onCreated, onFailure); + return; + } + } + + NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast(endpoint)); + onFailure(504, "No channels available to connect"); +} + void UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint) { @@ -126,6 +304,22 @@ UdpFactory::createChannel(const std::string& localIp, const std::string& localPo return createChannel(endpoint, timeout); } +std::vector> +UdpFactory::getChannels() const +{ + return getChannelsFromMap(m_channels); +} + +shared_ptr +UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const +{ + auto i = m_channels.find(localEndpoint); + if (i != m_channels.end()) + return i->second; + else + return nullptr; +} + shared_ptr UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint, const udp::Endpoint& multicastEndpoint, @@ -203,16 +397,16 @@ UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint, ": " + std::strerror(errno))); } } -#endif +#endif // __linux__ - auto linkService = make_unique(); - auto transport = make_unique(localEndpoint, multicastEndpoint, - std::move(receiveSocket), - std::move(sendSocket)); + auto linkService = make_unique(); + auto transport = make_unique(localEndpoint, multicastEndpoint, + std::move(receiveSocket), + std::move(sendSocket)); face = make_shared(std::move(linkService), std::move(transport)); - m_multicastFaces[localEndpoint] = face; - connectFaceClosedSignal(*face, [this, localEndpoint] { m_multicastFaces.erase(localEndpoint); }); + m_mcastFaces[localEndpoint] = face; + connectFaceClosedSignal(*face, [this, localEndpoint] { m_mcastFaces.erase(localEndpoint); }); return face; } @@ -230,87 +424,63 @@ UdpFactory::createMulticastFace(const std::string& localIp, return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName); } -void -UdpFactory::createFace(const FaceUri& uri, - ndn::nfd::FacePersistency persistency, - bool wantLocalFieldsEnabled, - const FaceCreatedCallback& onCreated, - const FaceCreationFailedCallback& onFailure) -{ - BOOST_ASSERT(uri.isCanonical()); - - if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) { - NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND"); - onFailure(406, "Outgoing unicast UDP faces do not support on-demand persistency"); - return; - } - - udp::Endpoint endpoint(ip::address::from_string(uri.getHost()), - boost::lexical_cast(uri.getPort())); - - if (endpoint.address().is_multicast()) { - NFD_LOG_TRACE("createFace does not support multicast faces"); - onFailure(406, "Cannot create multicast UDP faces"); - return; - } - - if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) { - NFD_LOG_TRACE("Requested endpoint is prohibited " - "(reserved by this NFD or disallowed by face management protocol)"); - onFailure(406, "Requested endpoint is prohibited"); - return; - } - - if (wantLocalFieldsEnabled) { - // UDP faces are never local - NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled"); - onFailure(406, "Local fields can only be enabled on faces with local scope"); - return; - } - - // very simple logic for now - for (const auto& i : m_channels) { - if ((i.first.address().is_v4() && endpoint.address().is_v4()) || - (i.first.address().is_v6() && endpoint.address().is_v6())) { - i.second->connect(endpoint, persistency, onCreated, onFailure); - return; - } - } - - NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast(endpoint)); - onFailure(504, "No channels available to connect"); -} - -std::vector> -UdpFactory::getChannels() const -{ - std::vector> channels; - channels.reserve(m_channels.size()); - - for (const auto& i : m_channels) - channels.push_back(i.second); - - return channels; -} - -shared_ptr -UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const -{ - auto i = m_channels.find(localEndpoint); - if (i != m_channels.end()) - return i->second; - else - return nullptr; -} - shared_ptr UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const { - auto i = m_multicastFaces.find(localEndpoint); - if (i != m_multicastFaces.end()) + auto i = m_mcastFaces.find(localEndpoint); + if (i != m_mcastFaces.end()) return i->second; else return nullptr; } +void +UdpFactory::applyMulticastConfig(const FaceSystem::ConfigContext& context) +{ + // collect old faces + std::set> oldFaces; + boost::copy(m_mcastFaces | boost::adaptors::map_values, + std::inserter(oldFaces, oldFaces.end())); + + if (m_mcastConfig.isEnabled) { + // determine interfaces on which faces should be created or retained + auto capableNetifRange = context.listNetifs() | + boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) { + return netif.isUp() && netif.isMulticastCapable() && + !netif.ipv4Addresses.empty(); + }); + + bool needIfname = false; +#ifdef __linux__ + std::vector capableNetifs; + boost::copy(capableNetifRange, std::back_inserter(capableNetifs)); + // on Linux, ifname is needed to create more than one UDP multicast face on the same group + needIfname = capableNetifs.size() > 1; +#else + auto& capableNetifs = capableNetifRange; +#endif // __linux__ + + // create faces + for (const auto& netif : capableNetifs) { + udp::Endpoint localEndpoint(netif.ipv4Addresses.front(), m_mcastConfig.group.port()); + shared_ptr face = this->createMulticastFace(localEndpoint, m_mcastConfig.group, + needIfname ? netif.name : ""); + if (face->getId() == INVALID_FACEID) { + // new face: register with forwarding + context.addFace(face); + } + else { + // existing face: don't destroy + oldFaces.erase(face); + } + } + } + + // destroy old faces that are not needed in new configuration + for (const auto& face : oldFaces) { + face->close(); + } +} + +} // namespace face } // namespace nfd diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp index c788a3f6..7ec42b36 100644 --- a/daemon/face/udp-factory.hpp +++ b/daemon/face/udp-factory.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (c) 2014-2016, Regents of the University of California, + * Copyright (c) 2014-2017, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -30,9 +30,13 @@ #include "udp-channel.hpp" namespace nfd { +namespace face { -/// @todo IPv6 multicast support not implemented - +/** \brief protocol factory for UDP over IPv4 and IPv6 + * + * UDP unicast is available over both IPv4 and IPv6. + * UDP multicast is available over IPv4 only. + */ class UdpFactory : public ProtocolFactory { public: @@ -49,7 +53,18 @@ public: } }; - typedef std::map> MulticastFaceMap; + /** \brief process face_system.udp config section + */ + void + processConfig(OptionalConfigSection configSection, + FaceSystem::ConfigContext& context) override; + + void + createFace(const FaceUri& uri, + ndn::nfd::FacePersistency persistency, + bool wantLocalFieldsEnabled, + const FaceCreatedCallback& onCreated, + const FaceCreationFailedCallback& onFailure) override; /** * \brief Create UDP-based channel using udp::Endpoint @@ -97,6 +112,9 @@ public: createChannel(const std::string& localIp, const std::string& localPort, const time::seconds& timeout = time::seconds(600)); + std::vector> + getChannels() const override; + /** * \brief Create MulticastUdpFace using udp::Endpoint * @@ -139,23 +157,6 @@ public: const std::string& multicastPort, const std::string& networkInterfaceName = ""); - /** - * \brief Get map of configured multicast faces - */ - const MulticastFaceMap& - getMulticastFaces() const; - -public: // from ProtocolFactory - virtual void - createFace(const FaceUri& uri, - ndn::nfd::FacePersistency persistency, - bool wantLocalFieldsEnabled, - const FaceCreatedCallback& onCreated, - const FaceCreationFailedCallback& onFailure) override; - - virtual std::vector> - getChannels() const override; - PUBLIC_WITH_TESTS_ELSE_PRIVATE: void prohibitEndpoint(const udp::Endpoint& endpoint); @@ -185,20 +186,26 @@ private: shared_ptr findMulticastFace(const udp::Endpoint& localEndpoint) const; + void + applyMulticastConfig(const FaceSystem::ConfigContext& context); + private: std::map> m_channels; - MulticastFaceMap m_multicastFaces; + + struct MulticastConfig + { + bool isEnabled = false; + udp::Endpoint group = udp::getDefaultMulticastGroup(); + }; + + MulticastConfig m_mcastConfig; + std::map> m_mcastFaces; PUBLIC_WITH_TESTS_ELSE_PRIVATE: std::set m_prohibitedEndpoints; }; -inline const UdpFactory::MulticastFaceMap& -UdpFactory::getMulticastFaces() const -{ - return m_multicastFaces; -} - +} // namespace face } // namespace nfd #endif // NFD_DAEMON_FACE_UDP_FACTORY_HPP diff --git a/daemon/face/udp-protocol.cpp b/daemon/face/udp-protocol.cpp index fb51a3a8..0d842bce 100644 --- a/daemon/face/udp-protocol.cpp +++ b/daemon/face/udp-protocol.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (c) 2014-2015, Regents of the University of California, + * Copyright (c) 2014-2017, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -29,7 +29,7 @@ namespace nfd { namespace udp { ssize_t -computeMtu(const boost::asio::ip::udp::endpoint& localEndpoint) +computeMtu(const Endpoint& localEndpoint) { size_t mtu = 0; if (localEndpoint.address().is_v4()) { // IPv4 diff --git a/daemon/face/udp-protocol.hpp b/daemon/face/udp-protocol.hpp index 5f7c25a4..f7166586 100644 --- a/daemon/face/udp-protocol.hpp +++ b/daemon/face/udp-protocol.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (c) 2014-2016, Regents of the University of California, + * Copyright (c) 2014-2017, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -31,10 +31,21 @@ namespace nfd { namespace udp { +typedef boost::asio::ip::udp::endpoint Endpoint; + /** \brief computes maximum payload size in a UDP packet */ ssize_t -computeMtu(const boost::asio::ip::udp::endpoint& localEndpoint); +computeMtu(const Endpoint& localEndpoint); + +/** \return default multicast group: 224.0.23.170:56363 + */ +inline Endpoint +getDefaultMulticastGroup() +{ + // 224.0.23.170:56363 + return {boost::asio::ip::address_v4(0xE00017AA), 56363}; +} } // namespace udp } // namespace nfd diff --git a/tests/daemon/face/face-system-fixture.hpp b/tests/daemon/face/face-system-fixture.hpp index 2198c7e0..42ed02cf 100644 --- a/tests/daemon/face/face-system-fixture.hpp +++ b/tests/daemon/face/face-system-fixture.hpp @@ -28,6 +28,7 @@ #include "face/face.hpp" #include "face/face-system.hpp" +#include "face/protocol-factory.hpp" #include "fw/face-table.hpp" #include "tests/test-common.hpp" diff --git a/tests/daemon/face/face-system.t.cpp b/tests/daemon/face/face-system.t.cpp index cd900785..de1b6b15 100644 --- a/tests/daemon/face/face-system.t.cpp +++ b/tests/daemon/face/face-system.t.cpp @@ -26,9 +26,6 @@ #include "face/face-system.hpp" #include "face-system-fixture.hpp" -// ProtocolFactory includes, sorted alphabetically -#include "face/udp-factory.hpp" - #include "tests/test-common.hpp" namespace nfd { @@ -199,207 +196,6 @@ BOOST_AUTO_TEST_CASE(ChangeProvidedSchemes) BOOST_AUTO_TEST_SUITE_END() // ProcessConfig -///\todo #3904 move Config* to *Factory test suite - -BOOST_AUTO_TEST_SUITE(ConfigUdp) - -BOOST_AUTO_TEST_CASE(Normal) -{ - SKIP_IF_NOT_SUPERUSER(); - - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - port 6363 - enable_v4 yes - enable_v6 yes - idle_timeout 30 - keep_alive_interval 25 - mcast yes - mcast_port 56363 - mcast_group 224.0.23.170 - } - } - )CONFIG"; - - BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true)); - BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false)); - - auto& factory = this->getFactoryByScheme("udp"); - BOOST_CHECK_EQUAL(factory.getChannels().size(), 2); -} - -BOOST_AUTO_TEST_CASE(BadIdleTimeout) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - idle_timeout hello - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(BadMcast) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - mcast hello - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(BadMcastGroup) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - mcast no - mcast_port 50 - mcast_group hello - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(BadMcastGroupV6) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - mcast no - mcast_port 50 - mcast_group ::1 - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(ChannelsDisabled) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - port 6363 - enable_v4 no - enable_v6 no - idle_timeout 30 - keep_alive_interval 25 - mcast yes - mcast_port 56363 - mcast_group 224.0.23.170 - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(ConflictingMcast) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - port 6363 - enable_v4 no - enable_v6 yes - idle_timeout 30 - keep_alive_interval 25 - mcast yes - mcast_port 56363 - mcast_group 224.0.23.170 - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(UnknownOption) -{ - const std::string CONFIG = R"CONFIG( - face_system - { - udp - { - hello - } - } - )CONFIG"; - - BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); - BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); -} - -BOOST_AUTO_TEST_CASE(MulticastReinit) -{ - SKIP_IF_NOT_SUPERUSER(); - - const std::string CONFIG_WITH_MCAST = R"CONFIG( - face_system - { - udp - { - mcast yes - } - } - )CONFIG"; - - BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false)); - - auto& factory = this->getFactoryByScheme("udp"); - - if (factory.getMulticastFaces().empty()) { - BOOST_WARN_MESSAGE(false, "skipping assertions that require at least one UDP multicast face"); - return; - } - - const std::string CONFIG_WITHOUT_MCAST = R"CONFIG( - face_system - { - udp - { - mcast no - } - } - )CONFIG"; - - BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false)); - BOOST_REQUIRE_NO_THROW(g_io.poll()); - BOOST_CHECK_EQUAL(factory.getMulticastFaces().size(), 0); -} -BOOST_AUTO_TEST_SUITE_END() // ConfigUdp - BOOST_AUTO_TEST_SUITE_END() // TestFaceSystem BOOST_AUTO_TEST_SUITE_END() // Mgmt diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp index d6ef39c9..ac468cb1 100644 --- a/tests/daemon/face/udp-factory.t.cpp +++ b/tests/daemon/face/udp-factory.t.cpp @@ -26,17 +26,343 @@ #include "face/udp-factory.hpp" #include "factory-test-common.hpp" -#include "core/network-interface.hpp" +#include "face-system-fixture.hpp" #include "tests/limited-io.hpp" namespace nfd { +namespace face { namespace tests { +using namespace nfd::tests; + BOOST_AUTO_TEST_SUITE(Face) BOOST_FIXTURE_TEST_SUITE(TestUdpFactory, BaseFixture) using nfd::Face; +BOOST_FIXTURE_TEST_SUITE(ProcessConfig, FaceSystemFixture) + +BOOST_AUTO_TEST_CASE(Channels) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + port 7001 + enable_v4 yes + enable_v6 yes + idle_timeout 30 + mcast no + } + } + )CONFIG"; + + parseConfig(CONFIG, true); + parseConfig(CONFIG, false); + + auto& factory = this->getFactoryById("udp"); + checkChannelListEqual(factory, {"udp4://0.0.0.0:7001", "udp6://[::]:7001"}); +} + +BOOST_AUTO_TEST_CASE(ChannelV4) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + port 7001 + enable_v4 yes + enable_v6 no + mcast no + } + } + )CONFIG"; + + parseConfig(CONFIG, true); + parseConfig(CONFIG, false); + + auto& factory = this->getFactoryById("udp"); + checkChannelListEqual(factory, {"udp4://0.0.0.0:7001"}); +} + +BOOST_AUTO_TEST_CASE(ChannelV6) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + port 7001 + enable_v4 no + enable_v6 yes + mcast no + } + } + )CONFIG"; + + parseConfig(CONFIG, true); + parseConfig(CONFIG, false); + + auto& factory = this->getFactoryById("udp"); + checkChannelListEqual(factory, {"udp6://[::]:7001"}); +} + +class UdpMcastConfigFixture : public FaceSystemFixture +{ +protected: + UdpMcastConfigFixture() + { + for (const auto& netif : listNetworkInterfaces()) { + if (netif.isUp() && netif.isMulticastCapable() && !netif.ipv4Addresses.empty()) { + netifs.push_back(netif); + } + } + } + + std::vector + listUdpMcastFaces() const + { + return this->listFacesByScheme("udp4", ndn::nfd::LINK_TYPE_MULTI_ACCESS); + } + + size_t + countUdpMcastFaces() const + { + return this->listUdpMcastFaces().size(); + } + +protected: + /** \brief MulticastUdpTransport-capable network interfaces + */ + std::vector netifs; +}; + +#define SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(n) \ + do { \ + if (this->netifs.size() < (n)) { \ + BOOST_WARN_MESSAGE(false, "skipping assertions that require " #n \ + " or more MulticastUdpTransport-capable network interfaces"); \ + return; \ + } \ + } while (false) + +BOOST_FIXTURE_TEST_CASE(EnableDisableMcast, UdpMcastConfigFixture) +{ +#ifdef __linux__ + // need superuser privilege for creating multicast faces on linux + SKIP_IF_NOT_SUPERUSER(); +#endif // __linux__ + + const std::string CONFIG_WITH_MCAST = R"CONFIG( + face_system + { + udp + { + mcast yes + } + } + )CONFIG"; + const std::string CONFIG_WITHOUT_MCAST = R"CONFIG( + face_system + { + udp + { + mcast no + } + } + )CONFIG"; + + parseConfig(CONFIG_WITHOUT_MCAST, false); + BOOST_CHECK_EQUAL(this->countUdpMcastFaces(), 0); + + SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1); + + parseConfig(CONFIG_WITH_MCAST, false); + g_io.poll(); + BOOST_CHECK_EQUAL(this->countUdpMcastFaces(), netifs.size()); + + parseConfig(CONFIG_WITHOUT_MCAST, false); + g_io.poll(); + BOOST_CHECK_EQUAL(this->countUdpMcastFaces(), 0); +} + +BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpoint, UdpMcastConfigFixture) +{ +#ifdef __linux__ + // need superuser privilege for creating multicast faces on linux + SKIP_IF_NOT_SUPERUSER(); +#endif // __linux__ + SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1); + + const std::string CONFIG1 = R"CONFIG( + face_system + { + udp + { + mcast_group 239.66.30.1 + mcast_port 7011 + } + } + )CONFIG"; + const std::string CONFIG2 = R"CONFIG( + face_system + { + udp + { + mcast_group 239.66.30.2 + mcast_port 7012 + } + } + )CONFIG"; + + parseConfig(CONFIG1, false); + auto udpMcastFaces = this->listUdpMcastFaces(); + BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), netifs.size()); + BOOST_CHECK_EQUAL(udpMcastFaces.front()->getRemoteUri(), + FaceUri("udp4://239.66.30.1:7011")); + + parseConfig(CONFIG2, false); + g_io.poll(); + udpMcastFaces = this->listUdpMcastFaces(); + BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), netifs.size()); + BOOST_CHECK_EQUAL(udpMcastFaces.front()->getRemoteUri(), + FaceUri("udp4://239.66.30.2:7012")); +} + +BOOST_AUTO_TEST_CASE(Omitted) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + } + )CONFIG"; + + parseConfig(CONFIG, true); + parseConfig(CONFIG, false); + + auto& factory = this->getFactoryById("udp"); + BOOST_CHECK_EQUAL(factory.getChannels().size(), 0); + BOOST_CHECK_EQUAL(this->listFacesByScheme("udp4", ndn::nfd::LINK_TYPE_MULTI_ACCESS).size(), 0); +} + +BOOST_AUTO_TEST_CASE(BadIdleTimeout) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + idle_timeout hello + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(BadMcast) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + mcast hello + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(BadMcastGroup) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + mcast_group hello + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(BadMcastGroupV4Unicast) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + mcast_group 10.0.0.1 + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(BadMcastGroupV6) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + mcast_group ff00::1 + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(AllDisabled) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + enable_v4 no + enable_v6 no + mcast no + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_CASE(UnknownOption) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + udp + { + hello + } + } + )CONFIG"; + + BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error); + BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error); +} + +BOOST_AUTO_TEST_SUITE_END() // ProcessConfig + BOOST_AUTO_TEST_CASE(GetChannels) { UdpFactory factory; @@ -277,4 +603,5 @@ BOOST_AUTO_TEST_SUITE_END() // TestUdpFactory BOOST_AUTO_TEST_SUITE_END() // Face } // namespace tests +} // namespace face } // namespace nfd