face: unicast support in EthernetFactory
Change-Id: I1886a87d79a7194b3320a5417404b17a7290fa5d
Refs: #4012
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 44282c0..20fda6d 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -49,6 +49,8 @@
{
// ether
// {
+ // listen yes
+ // idle_timeout 600
// mcast yes
// mcast_group 01:00:5E:00:17:AA
// mcast_ad_hoc no
@@ -61,6 +63,8 @@
// }
// }
+ bool wantListen = true;
+ uint32_t idleTimeout = 600;
MulticastConfig mcastConfig;
if (configSection) {
@@ -71,8 +75,14 @@
const std::string& key = pair.first;
const ConfigSection& value = pair.second;
- if (key == "mcast") {
- mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "ether");
+ if (key == "listen") {
+ wantListen = ConfigFile::parseYesNo(pair, "face_system.ether");
+ }
+ else if (key == "idle_timeout") {
+ idleTimeout = ConfigFile::parseNumber<uint32_t>(pair, "face_system.ether");
+ }
+ else if (key == "mcast") {
+ mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.ether");
}
else if (key == "mcast_group") {
const std::string& valueStr = value.get_value<std::string>();
@@ -87,7 +97,7 @@
}
}
else if (key == "mcast_ad_hoc") {
- bool wantAdHoc = ConfigFile::parseYesNo(pair, "ether");
+ bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.ether");
mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
}
else if (key == "whitelist") {
@@ -103,6 +113,32 @@
}
if (!context.isDryRun) {
+ if (configSection) {
+ providedSchemes.insert("ether");
+
+ // determine the interfaces on which channels should be created
+ auto netifs = context.listNetifs() |
+ boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
+ return netif.isUp() && !netif.isLoopback();
+ });
+
+ // create channels
+ for (const auto& netif : netifs) {
+ auto channel = this->createChannel(netif, time::seconds(idleTimeout));
+ if (wantListen && !channel->isListening()) {
+ try {
+ channel->listen(context.addFace, nullptr);
+ }
+ catch (const EthernetChannel::Error& e) {
+ NFD_LOG_WARN("Cannot listen on " << netif.name << ": " << e.what());
+ }
+ }
+ }
+ }
+ else if (!m_channels.empty()) {
+ NFD_LOG_WARN("Cannot disable dev channels after initialization");
+ }
+
if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
if (mcastConfig.isEnabled) {
NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
@@ -139,13 +175,66 @@
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure)
{
- onFailure(406, "Unsupported protocol");
+ BOOST_ASSERT(remoteUri.isCanonical());
+
+ if (!localUri || localUri->getScheme() != "dev") {
+ NFD_LOG_TRACE("Cannot create unicast Ethernet face without dev:// LocalUri");
+ onFailure(406, "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme");
+ return;
+ }
+ BOOST_ASSERT(localUri->isCanonical());
+
+ if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
+ NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
+ onFailure(406, "Outgoing Ethernet faces do not support on-demand persistency");
+ return;
+ }
+
+ ethernet::Address remoteEndpoint(ethernet::Address::fromString(remoteUri.getHost()));
+ std::string localEndpoint(localUri->getHost());
+
+ if (remoteEndpoint.isMulticast()) {
+ NFD_LOG_TRACE("createFace does not support multicast faces");
+ onFailure(406, "Cannot create multicast Ethernet faces");
+ return;
+ }
+
+ if (wantLocalFieldsEnabled) {
+ // Ethernet 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;
+ }
+
+ for (const auto& i : m_channels) {
+ if (i.first == localEndpoint) {
+ i.second->connect(remoteEndpoint, persistency, onCreated, onFailure);
+ return;
+ }
+ }
+
+ NFD_LOG_TRACE("No channels available to connect to " << remoteEndpoint);
+ onFailure(504, "No channels available to connect");
+}
+
+shared_ptr<EthernetChannel>
+EthernetFactory::createChannel(const NetworkInterfaceInfo& localEndpoint,
+ time::nanoseconds idleTimeout)
+{
+ auto it = m_channels.find(localEndpoint.name);
+ if (it != m_channels.end())
+ return it->second;
+
+ auto channel = std::make_shared<EthernetChannel>(localEndpoint, idleTimeout);
+ m_channels[localEndpoint.name] = channel;
+
+ return channel;
}
std::vector<shared_ptr<const Channel>>
EthernetFactory::getChannels() const
{
- return {};
+ return getChannelsFromMap(m_channels);
}
shared_ptr<Face>
@@ -197,8 +286,7 @@
face = this->createMulticastFace(netif, m_mcastConfig.group);
}
catch (const EthernetTransport::Error& e) {
- NFD_LOG_ERROR("Cannot create Ethernet multicast face on " << netif.name << ": " <<
- e.what() << ", continuing");
+ NFD_LOG_WARN("Cannot create Ethernet multicast face on " << netif.name << ": " << e.what());
continue;
}
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index 856116f..1cbe4e9 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -27,13 +27,12 @@
#define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
#include "protocol-factory.hpp"
+#include "ethernet-channel.hpp"
namespace nfd {
namespace face {
/** \brief protocol factory for Ethernet
- *
- * Currently, only Ethernet multicast is supported.
*/
class EthernetFactory : public ProtocolFactory
{
@@ -47,8 +46,6 @@
processConfig(OptionalConfigSection configSection,
FaceSystem::ConfigContext& context) override;
- /** \brief unicast face creation is not supported and will always fail
- */
void
createFace(const FaceUri& remoteUri,
const ndn::optional<FaceUri>& localUri,
@@ -57,26 +54,45 @@
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
- /** \return empty container, because Ethernet unicast is not supported
+ /**
+ * \brief Create Ethernet-based channel on the specified network interface
+ *
+ * If this method is called twice with the same endpoint, only one channel
+ * will be created. The second call will just return the existing channel.
+ *
+ * \return always a valid pointer to a EthernetChannel object, an exception
+ * is thrown if it cannot be created.
+ * \throw PcapHelper::Error
*/
+ shared_ptr<EthernetChannel>
+ createChannel(const NetworkInterfaceInfo& localEndpoint,
+ time::nanoseconds idleTimeout);
+
std::vector<shared_ptr<const Channel>>
getChannels() const override;
-private:
- /** \brief Create a face to communicate on the given Ethernet multicast group
- * \param netif local network interface
- * \param group multicast group address
- * \note Calling this method again with same arguments returns the existing face on the given
- * interface and multicast group rather than creating a new one.
- * \throw EthernetTransport::Error transport creation fails
+ /**
+ * \brief Create a face to communicate on the given Ethernet multicast group
+ *
+ * If this method is called twice with the same arguments, only one face
+ * will be created. The second call will just return the existing face.
+ *
+ * \param netif local network interface
+ * \param group multicast group address
+ *
+ * \throw EthernetTransport::Error transport creation fails
*/
shared_ptr<Face>
- createMulticastFace(const NetworkInterfaceInfo& netif, const ethernet::Address& group);
+ createMulticastFace(const NetworkInterfaceInfo& netif,
+ const ethernet::Address& group);
+private:
void
applyConfig(const FaceSystem::ConfigContext& context);
private:
+ std::map<std::string, shared_ptr<EthernetChannel>> m_channels; ///< ifname => channel
+
struct MulticastConfig
{
bool isEnabled = false;
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 49f1307..3132796 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -144,11 +144,8 @@
tcp::Endpoint endpoint(ip::address::from_string(remoteUri.getHost()),
boost::lexical_cast<uint16_t>(remoteUri.getPort()));
- if (endpoint.address().is_multicast()) {
- NFD_LOG_TRACE("createFace does not support multicast faces");
- onFailure(406, "Cannot create multicast TCP faces");
- return;
- }
+ // a canonical tcp4/tcp6 FaceUri cannot have a multicast address
+ BOOST_ASSERT(!endpoint.address().is_multicast());
if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
NFD_LOG_TRACE("Requested endpoint is prohibited "
@@ -172,7 +169,7 @@
}
}
- NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+ NFD_LOG_TRACE("No channels available to connect to " << endpoint);
onFailure(504, "No channels available to connect");
}
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index 35a51d7..4576c65 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -59,15 +59,11 @@
*
* tcp::Endpoint is really an alias for boost::asio::ip::tcp::endpoint.
*
- * If this method called twice with the same endpoint, only one channel
- * will be created. The second call will just retrieve the existing
- * channel.
+ * If this method is called twice with the same endpoint, only one channel
+ * will be created. The second call will just return the existing channel.
*
- * \returns always a valid pointer to a TcpChannel object, an exception
- * is thrown if it cannot be created.
- *
- * \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__tcp/endpoint.html
- * for details on ways to create tcp::Endpoint
+ * \return always a valid pointer to a TcpChannel object, an exception
+ * is thrown if it cannot be created.
*/
shared_ptr<TcpChannel>
createChannel(const tcp::Endpoint& localEndpoint);
@@ -75,10 +71,11 @@
/**
* \brief Create TCP-based channel using specified IP address and port number
*
- * This method is just a helper that converts a string representation of localIp and port to
- * tcp::Endpoint and calls the other createChannel overload.
+ * This method is just a helper that converts the string representation of \p localIp
+ * and \p localPort to tcp::Endpoint and calls the other createChannel overload.
*
- * \throws std::runtime_error
+ * \return always a valid pointer to a UdpChannel object, an exception
+ * is thrown if it cannot be created.
*/
shared_ptr<TcpChannel>
createChannel(const std::string& localIp, const std::string& localPort);
@@ -100,8 +97,8 @@
/**
* \brief Look up TcpChannel using specified local endpoint
*
- * \returns shared pointer to the existing TcpChannel object
- * or empty shared pointer when such channel does not exist
+ * \return shared pointer to the existing TcpChannel object
+ * or empty shared pointer when such channel does not exist
*/
shared_ptr<TcpChannel>
findChannel(const tcp::Endpoint& localEndpoint) const;
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index a544edf..5b68061 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -219,7 +219,7 @@
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");
+ onFailure(406, "Outgoing UDP faces do not support on-demand persistency");
return;
}
@@ -255,7 +255,7 @@
}
}
- NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+ NFD_LOG_TRACE("No channels available to connect to " << endpoint);
onFailure(504, "No channels available to connect");
}
@@ -307,38 +307,38 @@
}
shared_ptr<UdpChannel>
-UdpFactory::createChannel(const udp::Endpoint& endpoint,
- const time::seconds& timeout)
+UdpFactory::createChannel(const udp::Endpoint& localEndpoint,
+ time::nanoseconds idleTimeout)
{
- auto channel = findChannel(endpoint);
- if (channel)
- return channel;
+ auto it = m_channels.find(localEndpoint);
+ if (it != m_channels.end())
+ return it->second;
- if (endpoint.address().is_multicast()) {
+ if (localEndpoint.address().is_multicast()) {
BOOST_THROW_EXCEPTION(Error("createChannel is only for unicast channels. The provided endpoint "
"is multicast. Use createMulticastFace to create a multicast face"));
}
// check if the endpoint is already used by a multicast face
- auto face = findMulticastFace(endpoint);
- if (face) {
+ if (m_mcastFaces.find(localEndpoint) != m_mcastFaces.end()) {
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
"endpoint is already allocated for a UDP multicast face"));
}
- channel = std::make_shared<UdpChannel>(endpoint, timeout);
- m_channels[endpoint] = channel;
- prohibitEndpoint(endpoint);
+ auto channel = std::make_shared<UdpChannel>(localEndpoint, idleTimeout);
+ m_channels[localEndpoint] = channel;
+ prohibitEndpoint(localEndpoint);
+
return channel;
}
shared_ptr<UdpChannel>
UdpFactory::createChannel(const std::string& localIp, const std::string& localPort,
- const time::seconds& timeout)
+ time::nanoseconds idleTimeout)
{
udp::Endpoint endpoint(ip::address::from_string(localIp),
boost::lexical_cast<uint16_t>(localPort));
- return createChannel(endpoint, timeout);
+ return createChannel(endpoint, idleTimeout);
}
std::vector<shared_ptr<const Channel>>
@@ -347,26 +347,16 @@
return getChannelsFromMap(m_channels);
}
-shared_ptr<UdpChannel>
-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<Face>
UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
const udp::Endpoint& multicastEndpoint,
- const std::string& networkInterfaceName/* = ""*/)
+ const std::string& networkInterfaceName)
{
// checking if the local and multicast endpoints are already in use for a multicast face
- auto face = findMulticastFace(localEndpoint);
- if (face) {
- if (face->getRemoteUri() == FaceUri(multicastEndpoint))
- return face;
+ auto it = m_mcastFaces.find(localEndpoint);
+ if (it != m_mcastFaces.end()) {
+ if (it->second->getRemoteUri() == FaceUri(multicastEndpoint))
+ return it->second;
else
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
"endpoint is already allocated for a UDP multicast face "
@@ -374,8 +364,7 @@
}
// checking if the local endpoint is already in use for a unicast channel
- auto unicastCh = findChannel(localEndpoint);
- if (unicastCh) {
+ if (m_channels.find(localEndpoint) != m_channels.end()) {
BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
"endpoint is already allocated for a UDP unicast channel"));
}
@@ -441,7 +430,7 @@
std::move(receiveSocket),
std::move(sendSocket),
m_mcastConfig.linkType);
- face = make_shared<Face>(std::move(linkService), std::move(transport));
+ auto face = make_shared<Face>(std::move(linkService), std::move(transport));
m_mcastFaces[localEndpoint] = face;
connectFaceClosedSignal(*face, [this, localEndpoint] { m_mcastFaces.erase(localEndpoint); });
@@ -453,7 +442,7 @@
UdpFactory::createMulticastFace(const std::string& localIp,
const std::string& multicastIp,
const std::string& multicastPort,
- const std::string& networkInterfaceName/* = ""*/)
+ const std::string& networkInterfaceName)
{
udp::Endpoint localEndpoint(ip::address::from_string(localIp),
boost::lexical_cast<uint16_t>(multicastPort));
@@ -462,16 +451,6 @@
return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
}
-shared_ptr<Face>
-UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
-{
- auto i = m_mcastFaces.find(localEndpoint);
- if (i != m_mcastFaces.end())
- return i->second;
- else
- return nullptr;
-}
-
void
UdpFactory::applyMulticastConfig(const FaceSystem::ConfigContext& context)
{
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index ce6d2e5..3ebf0a3 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -75,46 +75,37 @@
*
* udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
*
- * If this method called twice with the same endpoint, only one channel
- * will be created. The second call will just retrieve the existing
- * channel.
+ * If this method is called twice with the same endpoint, only one channel
+ * will be created. The second call will just return the existing channel.
*
* If a multicast face is already active on the same local endpoint,
- * the creation fails and an exception is thrown
+ * the creation fails and an exception is thrown.
*
- * Once a face is created, if it doesn't send/receive anything for
- * a period of time equal to timeout, it will be destroyed
- * @todo this funcionality has to be implemented
- *
- * \returns always a valid pointer to a UdpChannel object, an exception
- * is thrown if it cannot be created.
- *
- * \throws UdpFactory::Error
- *
- * \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__udp/endpoint.html
- * for details on ways to create udp::Endpoint
+ * \return always a valid pointer to a UdpChannel object, an exception
+ * is thrown if it cannot be created.
+ * \throw UdpFactory::Error
*/
shared_ptr<UdpChannel>
createChannel(const udp::Endpoint& localEndpoint,
- const time::seconds& timeout = time::seconds(600));
+ time::nanoseconds idleTimeout);
/**
* \brief Create UDP-based channel using specified IP address and port number
*
- * This method is just a helper that converts a string representation of localIp and port to
- * udp::Endpoint and calls the other createChannel overload.
+ * This method is just a helper that converts the string representation of \p localIp
+ * and \p localPort to udp::Endpoint and calls the other createChannel overload.
*
- * If localHost is a IPv6 address of a specific device, it must be in the form:
- * ip address%interface name
- * Example: fe80::5e96:9dff:fe7d:9c8d%en1
- * Otherwise, you can use ::
+ * If \p localIp is an IPv6 address of a specific device, it must be in the form
+ * <tt>[ip address]%[interface name]</tt>, e.g. <tt>"fe80::5e96:9dff:fe7d:9c8d%en1"</tt>.
+ * Otherwise, you can use <tt>"::"</tt>.
*
- * \throws UdpChannel::Error if the bind on the socket fails
- * \throws UdpFactory::Error
+ * \return always a valid pointer to a UdpChannel object, an exception
+ * is thrown if it cannot be created.
+ * \throw UdpFactory::Error
*/
shared_ptr<UdpChannel>
createChannel(const std::string& localIp, const std::string& localPort,
- const time::seconds& timeout = time::seconds(600));
+ time::nanoseconds idleTimeout = time::seconds(600));
std::vector<shared_ptr<const Channel>>
getChannels() const override;
@@ -124,14 +115,13 @@
*
* udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
*
- * The face will join the multicast group
+ * The face will join the specified multicast group.
*
- * If this method called twice with the same endpoint and group, only one face
- * will be created. The second call will just retrieve the existing
- * channel.
+ * If this method is called twice with the same pair of endpoints, only one
+ * face will be created. The second call will just return the existing face.
*
* If an unicast face is already active on the same local NIC and port, the
- * creation fails and an exception is thrown
+ * creation fails and an exception is thrown.
*
* \param localEndpoint local endpoint
* \param multicastEndpoint multicast endpoint
@@ -141,14 +131,9 @@
* An empty string can be provided in other system or in linux machine with only one
* MulticastUdpFace per multicast group
*
- *
- * \returns always a valid pointer to a MulticastUdpFace object, an exception
- * is thrown if it cannot be created.
- *
- * \throws UdpFactory::Error
- *
- * \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__udp/endpoint.html
- * for details on ways to create udp::Endpoint
+ * \return always a valid pointer to a MulticastUdpFace object, an exception
+ * is thrown if it cannot be created.
+ * \throw UdpFactory::Error
*/
shared_ptr<Face>
createMulticastFace(const udp::Endpoint& localEndpoint,
@@ -172,24 +157,6 @@
prohibitAllIpv6Endpoints(uint16_t port);
private:
- /**
- * \brief Look up UdpChannel using specified local endpoint
- *
- * \returns shared pointer to the existing UdpChannel object
- * or nullptr when such channel does not exist
- */
- shared_ptr<UdpChannel>
- findChannel(const udp::Endpoint& localEndpoint) const;
-
- /**
- * \brief Look up multicast UdpFace using specified local endpoint
- *
- * \returns shared pointer to the existing multicast UdpFace object
- * or nullptr when such face does not exist
- */
- shared_ptr<Face>
- findMulticastFace(const udp::Endpoint& localEndpoint) const;
-
void
applyMulticastConfig(const FaceSystem::ConfigContext& context);