face: UDP face/channel/factory
Change-Id: I4683b45378637133982efd23edd16a0c35148948
refs #1189
diff --git a/daemon/face/datagram-face.hpp b/daemon/face/datagram-face.hpp
new file mode 100644
index 0000000..f4f9a18
--- /dev/null
+++ b/daemon/face/datagram-face.hpp
@@ -0,0 +1,267 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_DATAGRAM_FACE_HPP
+#define NFD_FACE_DATAGRAM_FACE_HPP
+
+#include "face.hpp"
+
+namespace nfd {
+
+template <class T>
+class DatagramFace : public Face
+{
+public:
+ typedef T protocol;
+
+ explicit
+ DatagramFace(const shared_ptr<typename protocol::socket>& socket);
+
+ virtual
+ ~DatagramFace();
+
+ // from Face
+ virtual void
+ sendInterest(const Interest& interest);
+
+ virtual void
+ sendData(const Data& data);
+
+ virtual void
+ close();
+
+ void
+ handleSend(const boost::system::error_code& error,
+ const Block& wire);
+
+ void
+ handleReceive(const boost::system::error_code& error,
+ size_t nBytesReceived);
+
+protected:
+
+ void
+ receiveDatagram(const uint8_t* buffer,
+ size_t nBytesReceived,
+ const boost::system::error_code& error);
+
+ void
+ keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
+
+ void
+ closeSocket();
+
+protected:
+ shared_ptr<typename protocol::socket> m_socket;
+ uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
+
+ NFD_LOG_INCLASS_DECLARE();
+
+};
+
+template <class T>
+inline
+DatagramFace<T>::DatagramFace(const shared_ptr<typename DatagramFace::protocol::socket>& socket)
+ : m_socket(socket)
+{
+ m_socket->async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
+ bind(&DatagramFace<T>::handleReceive, this, _1, _2));
+}
+
+template <class T>
+inline
+DatagramFace<T>::~DatagramFace()
+{
+}
+
+template <class T>
+inline void
+DatagramFace<T>::sendInterest(const Interest& interest)
+{
+ m_socket->async_send(boost::asio::buffer(interest.wireEncode().wire(),
+ interest.wireEncode().size()),
+ bind(&DatagramFace<T>::handleSend, this, _1, interest.wireEncode()));
+
+ // anything else should be done here?
+}
+
+template <class T>
+inline void
+DatagramFace<T>::sendData(const Data& data)
+{
+ m_socket->async_send(boost::asio::buffer(data.wireEncode().wire(),
+ data.wireEncode().size()),
+ bind(&DatagramFace<T>::handleSend, this, _1, data.wireEncode()));
+
+ // anything else should be done here?
+}
+
+template <class T>
+inline void
+DatagramFace<T>::handleSend(const boost::system::error_code& error,
+ const Block& wire)
+{
+ if (error != 0) {
+ if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+ return;
+
+ if (!m_socket->is_open())
+ {
+ onFail("Tunnel closed");
+ return;
+ }
+
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Send operation failed, closing socket: "
+ << error.category().message(error.value()));
+
+ closeSocket();
+
+ if (error == boost::asio::error::eof)
+ {
+ onFail("Tunnel closed");
+ }
+ else
+ {
+ onFail("Send operation failed, closing socket: " +
+ error.category().message(error.value()));
+ }
+ return;
+ }
+
+ NFD_LOG_TRACE("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Successfully sent: " << wire.size() << " bytes");
+ // do nothing (needed to retain validity of wire memory block
+}
+
+template <class T>
+inline void
+DatagramFace<T>::close()
+{
+ if (!m_socket->is_open())
+ return;
+
+ NFD_LOG_INFO("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Close tunnel");
+
+ closeSocket();
+ onFail("Close tunnel");
+}
+
+template <class T>
+inline void
+DatagramFace<T>::handleReceive(const boost::system::error_code& error,
+ size_t nBytesReceived)
+{
+ receiveDatagram(m_inputBuffer, nBytesReceived, error);
+ m_socket->async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
+ bind(&DatagramFace<T>::handleReceive, this, _1, _2));
+}
+
+template <class T>
+inline void
+DatagramFace<T>::receiveDatagram(const uint8_t* buffer,
+ size_t nBytesReceived,
+ const boost::system::error_code& error)
+{
+ if (error != 0 || nBytesReceived == 0) {
+ if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+ return;
+
+ // this should be unnecessary, but just in case
+ if (!m_socket->is_open())
+ {
+ onFail("Tunnel closed");
+ return;
+ }
+
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Receive operation failed: "
+ << error.category().message(error.value()));
+
+ closeSocket();
+
+ if (error == boost::asio::error::eof)
+ {
+ onFail("Tunnel closed");
+ }
+ else
+ {
+ onFail("Receive operation failed, closing socket: " +
+ error.category().message(error.value()));
+ }
+ return;
+ }
+
+ NFD_LOG_TRACE("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received: " << nBytesReceived << " bytes");
+
+ /// @todo Eliminate reliance on exceptions in this path
+ try {
+ Block element(buffer, nBytesReceived);
+
+ if (element.size() != nBytesReceived)
+ {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received datagram size and decoded "
+ << "element size don't match");
+ /// @todo this message should not extend the face lifetime
+ return;
+ }
+ if (!this->decodeAndDispatchInput(element))
+ {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received unrecognized block of type ["
+ << element.type() << "]");
+ // ignore unknown packet and proceed
+ /// @todo this message should not extend the face lifetime
+ return;
+ }
+ }
+ catch(const tlv::Error& e) {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received input is invalid");
+ /// @todo this message should not extend the face lifetime
+ return;
+ }
+}
+
+
+template <class T>
+inline void
+DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
+{
+}
+
+template <class T>
+inline void
+DatagramFace<T>::closeSocket()
+{
+ boost::asio::io_service& io = m_socket->get_io_service();
+
+ // use the non-throwing variants and ignore errors, if any
+ boost::system::error_code error;
+ m_socket->shutdown(protocol::socket::shutdown_both, error);
+ m_socket->close(error);
+ // after this, handlers will be called with an error code
+
+ // ensure that the Face object is alive at least until all pending
+ // handlers are dispatched
+ io.post(bind(&DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted,
+ this, this->shared_from_this()));
+}
+
+} // namespace nfd
+
+#endif // NFD_FACE_DATAGRAM_FACE_HPP
diff --git a/daemon/face/multicast-udp-face.cpp b/daemon/face/multicast-udp-face.cpp
new file mode 100644
index 0000000..edd2ff4
--- /dev/null
+++ b/daemon/face/multicast-udp-face.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "multicast-udp-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("MulticastUdpFace");
+
+
+MulticastUdpFace::MulticastUdpFace(const shared_ptr<MulticastUdpFace::protocol::socket>& socket)
+ : UdpFace(socket)
+{
+ NFD_LOG_DEBUG("Face creation. Multicast group: "
+ << m_socket->local_endpoint());
+ m_multicastGroup = m_socket->local_endpoint();
+}
+
+const boost::asio::ip::udp::endpoint&
+MulticastUdpFace::getMulticastGroup() const
+{
+ return m_multicastGroup;
+}
+
+void
+MulticastUdpFace::sendInterest(const Interest& interest)
+{
+ NFD_LOG_DEBUG("Sending interest");
+ m_socket->async_send_to(boost::asio::buffer(interest.wireEncode().wire(),
+ interest.wireEncode().size()),
+ m_multicastGroup,
+ bind(&DatagramFace<protocol>::handleSend, this, _1, interest.wireEncode()));
+
+ // anything else should be done here?
+}
+
+void
+MulticastUdpFace::sendData(const Data& data)
+{
+ NFD_LOG_DEBUG("Sending data");
+ m_socket->async_send_to(boost::asio::buffer(data.wireEncode().wire(),
+ data.wireEncode().size()),
+ m_multicastGroup,
+ bind(&DatagramFace<protocol>::handleSend, this, _1, data.wireEncode()));
+
+ // anything else should be done here?
+}
+
+bool
+MulticastUdpFace::isMultiAccess() const
+{
+ return true;
+}
+
+} // namespace nfd
diff --git a/daemon/face/multicast-udp-face.hpp b/daemon/face/multicast-udp-face.hpp
new file mode 100644
index 0000000..46404da
--- /dev/null
+++ b/daemon/face/multicast-udp-face.hpp
@@ -0,0 +1,49 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_MULTICAST_UDP_FACE_HPP
+#define NFD_FACE_MULTICAST_UDP_FACE_HPP
+
+#include "udp-face.hpp"
+
+namespace nfd
+{
+
+/**
+ * \brief Implementation of Face abstraction that uses
+ * multicast UDP as underlying transport mechanism
+ */
+class MulticastUdpFace : public UdpFace
+{
+public:
+ typedef boost::asio::ip::udp protocol;
+
+ /**
+ * \brief Creates a Udp face for multicast communication
+ */
+ explicit
+ MulticastUdpFace(const shared_ptr<protocol::socket>& socket);
+
+ const boost::asio::ip::udp::endpoint&
+ getMulticastGroup() const;
+
+ // from Face
+ virtual void
+ sendInterest(const Interest& interest);
+
+ virtual void
+ sendData(const Data& data);
+
+ virtual bool
+ isMultiAccess() const;
+
+private:
+ boost::asio::ip::udp::endpoint m_multicastGroup;
+};
+
+} // namespace nfd
+
+#endif // NFD_FACE_MULTICAST_UDP_FACE_HPP
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
new file mode 100644
index 0000000..2127cd6
--- /dev/null
+++ b/daemon/face/udp-channel.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "udp-channel.hpp"
+#include "core/global-io.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("UdpChannel");
+
+using namespace boost::asio;
+
+UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
+ const time::Duration& timeout)
+ : m_localEndpoint(localEndpoint)
+ , m_isListening(false)
+{
+ /// \todo the reuse_address works as we want in Linux, but in other system could be different.
+ /// We need to check this
+ /// (SO_REUSEADDR doesn't behave uniformly in different OS)
+
+ m_socket = make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
+ m_socket->open(m_localEndpoint.protocol());
+ m_socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
+
+ try {
+ m_socket->bind(m_localEndpoint);
+ }
+ catch (boost::system::system_error& e) {
+ //The bind failed, so the socket is useless now
+ m_socket->close();
+ throw Error("Failed to properly configure the socket. "
+ "UdpChannel creation aborted, check the address (" + std::string(e.what()) + ")");
+ }
+}
+
+void
+UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onListenFailed)
+{
+ if (m_isListening) {
+ throw Error("Listen already called on this channel");
+ }
+ m_isListening = true;
+
+ onFaceCreatedNewPeerCallback = onFaceCreated;
+ onConnectFailedNewPeerCallback = onListenFailed;
+
+ m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
+ m_newRemoteEndpoint,
+ bind(&UdpChannel::newPeer, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+}
+
+
+void
+UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
+ const FaceCreatedCallback& onFaceCreated)
+{
+ ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
+ if (i != m_channelFaces.end()) {
+ onFaceCreated(i->second);
+ return;
+ }
+
+ //creating a new socket for the face that will be created soon
+ shared_ptr<ip::udp::socket> clientSocket =
+ make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
+
+ clientSocket->open(m_localEndpoint.protocol());
+ clientSocket->set_option(ip::udp::socket::reuse_address(true));
+
+ try {
+ clientSocket->bind(m_localEndpoint);
+ clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
+ //(since there is no handshake the connect shouldn't block). If we go for
+ //async_connect, make sure that if in the meantime we receive a UDP pkt from
+ //that endpoint nothing bad happen (it's difficult, but it could happen)
+ }
+ catch (boost::system::system_error& e) {
+ clientSocket->close();
+ throw Error("Failed to properly configure the socket. Check the address ("
+ + std::string(e.what()) + ")");
+ }
+ createFace(clientSocket, onFaceCreated);
+}
+
+void
+UdpChannel::connect(const std::string& remoteHost,
+ const std::string& remotePort,
+ const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onConnectFailed)
+{
+ ip::udp::resolver::query query(remoteHost, remotePort);
+ shared_ptr<ip::udp::resolver> resolver =
+ make_shared<ip::udp::resolver>(boost::ref(getGlobalIoService()));
+
+ resolver->async_resolve(query,
+ bind(&UdpChannel::handleEndpointResolution, this, _1, _2,
+ onFaceCreated, onConnectFailed,
+ resolver));
+}
+
+void
+UdpChannel::handleEndpointResolution(const boost::system::error_code& error,
+ ip::udp::resolver::iterator remoteEndpoint,
+ const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onConnectFailed,
+ const shared_ptr<ip::udp::resolver>& resolver)
+{
+ if (error != 0 ||
+ remoteEndpoint == ip::udp::resolver::iterator())
+ {
+ if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
+ return;
+
+ NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
+ << error.category().message(error.value()));
+
+ onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
+ error.category().message(error.value()));
+ return;
+ }
+
+ connect(*remoteEndpoint, onFaceCreated);
+}
+
+size_t
+UdpChannel::size() const
+{
+ return m_channelFaces.size();
+}
+
+
+shared_ptr<UdpFace>
+UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
+ const FaceCreatedCallback& onFaceCreated)
+{
+ udp::Endpoint remoteEndpoint = socket->remote_endpoint();
+
+ shared_ptr<UdpFace> face = make_shared<UdpFace>(boost::cref(socket));
+ face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
+
+ onFaceCreated(face);
+ m_channelFaces[remoteEndpoint] = face;
+ return face;
+}
+
+void
+UdpChannel::newPeer(const boost::system::error_code& error,
+ std::size_t nBytesReceived)
+{
+ NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
+ ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
+ if (i != m_channelFaces.end()) {
+ //The face already exists.
+ //Usually this shouldn't happen, because the channel creates a Udpface
+ //as soon as it receives a pkt from a new endpoint and then the
+ //traffic is dispatched by the kernel directly to the face.
+ //However, if the node receives multiple packets from the same endpoint
+ //"at the same time", while the channel is creating the face the kernel
+ //could dispatch the other pkts to the channel because the face is not yet
+ //ready. In this case, the channel has to pass the pkt to the face
+ NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
+ << m_newRemoteEndpoint
+ << " is in progress");
+ //Passing the message to the correspondent face
+ i->second->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
+ return;
+ }
+
+ shared_ptr<ip::udp::socket> clientSocket =
+ make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
+ clientSocket->open(m_localEndpoint.protocol());
+ clientSocket->set_option(ip::udp::socket::reuse_address(true));
+ clientSocket->bind(m_localEndpoint);
+ clientSocket->connect(m_newRemoteEndpoint);
+
+ shared_ptr<UdpFace> newFace = createFace(clientSocket, onFaceCreatedNewPeerCallback);
+
+ //Passing the message to the correspondent face
+ newFace->handleFirstReceive(m_inputBuffer, nBytesReceived, error);
+
+ m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE),
+ m_newRemoteEndpoint,
+ bind(&UdpChannel::newPeer, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+}
+
+
+void UdpChannel::afterFaceFailed(udp::Endpoint &endpoint)
+{
+ NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
+ m_channelFaces.erase(endpoint);
+}
+
+} // namespace nfd
diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp
new file mode 100644
index 0000000..b987b5e
--- /dev/null
+++ b/daemon/face/udp-channel.hpp
@@ -0,0 +1,164 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_UDP_CHANNEL_HPP
+#define NFD_FACE_UDP_CHANNEL_HPP
+
+#include "common.hpp"
+#include "core/time.hpp"
+#include "udp-face.hpp"
+
+namespace nfd {
+
+namespace udp {
+ typedef boost::asio::ip::udp::endpoint Endpoint;
+} // namespace udp
+
+/**
+ * \brief Class implementing UDP-based channel to create faces
+ *
+ *
+ */
+class UdpChannel
+{
+public:
+
+ /**
+ * \brief Exception of UdpChannel
+ */
+ struct Error : public std::runtime_error
+ {
+ Error(const std::string& what) : runtime_error(what) {}
+ };
+
+ /**
+ * \brief Prototype for the callback called when face is created
+ * (as a response to new incoming communication not managed
+ * by any faces yet or after the connect function is created)
+ */
+ typedef function<void(const shared_ptr<UdpFace>& newFace)> FaceCreatedCallback;
+
+ /**
+ * \brief Prototype for the callback that is called when face is failed to
+ * get created
+ */
+ typedef function<void(const std::string& reason)> ConnectFailedCallback;
+
+ /**
+ * \brief Create UDP channel for the local endpoint
+ *
+ * To enable creation of faces upon incoming connections,
+ * one needs to explicitly call UdpChannel::listen method.
+ * The created socket is bound to the localEndpoint.
+ * reuse_address option is set
+ *
+ * \throw UdpChannel::Error if bind on the socket fails
+ */
+ UdpChannel(const udp::Endpoint& localEndpoint,
+ const time::Duration& timeout);
+
+ /**
+ * \brief Enable listening on the local endpoint, accept connections,
+ * and create faces when remote host makes a connection
+ * \param onFaceCreated Callback to notify successful creation of the face
+ * \param onAcceptFailed Callback to notify when channel fails
+ *
+ * \throws UdpChannel::Error if called multiple times
+ */
+ void
+ listen(const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onAcceptFailed);
+
+ /**
+ * \brief Create a face by establishing connection to remote endpoint
+ *
+ * \throw UdpChannel::Error if bind or connect on the socket fail
+ */
+ void
+ connect(const udp::Endpoint& remoteEndpoint,
+ const FaceCreatedCallback& onFaceCreated);
+ /**
+ * \brief Create a face by establishing connection to the specified
+ * remote host and remote port
+ *
+ * This method will never block and will return immediately. All
+ * necessary hostname and port resolution and connection will happen
+ * in asynchronous mode.
+ *
+ * If connection cannot be established within specified timeout, it
+ * will be aborted.
+ */
+ void
+ connect(const std::string& remoteHost, const std::string& remotePort,
+ const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onConnectFailed);
+
+ /**
+ * \brief Get number of faces in the channel
+ */
+ size_t
+ size() const;
+
+private:
+ shared_ptr<UdpFace>
+ createFace(const shared_ptr<boost::asio::ip::udp::socket>& socket,
+ const FaceCreatedCallback& onFaceCreated);
+ void
+ afterFaceFailed(udp::Endpoint& endpoint);
+
+ /**
+ * \brief The UdpChannel has received a new pkt from a remote endpoint not yet
+ * associated with any UdpFace
+ */
+ void
+ newPeer(const boost::system::error_code& error,
+ std::size_t nBytesReceived);
+
+ void
+ handleEndpointResolution(const boost::system::error_code& error,
+ boost::asio::ip::udp::resolver::iterator remoteEndpoint,
+ const FaceCreatedCallback& onFaceCreated,
+ const ConnectFailedCallback& onConnectFailed,
+ const shared_ptr<boost::asio::ip::udp::resolver>& resolver);
+
+private:
+ udp::Endpoint m_localEndpoint;
+
+ /**
+ * \brief Endpoint used to store the information about the last new remote endpoint
+ */
+ udp::Endpoint m_newRemoteEndpoint;
+
+ /**
+ * Callbacks for face creation.
+ * New communications are detected using async_receive_from.
+ * Its handler has a fixed signature. No space for the face callback
+ */
+ FaceCreatedCallback onFaceCreatedNewPeerCallback;
+
+ // @todo remove the onConnectFailedNewPeerCallback if it remains unused
+ ConnectFailedCallback onConnectFailedNewPeerCallback;
+
+ /**
+ * \brief Socket used to "accept" new communication
+ **/
+ shared_ptr<boost::asio::ip::udp::socket> m_socket;
+
+ uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
+
+ typedef std::map< udp::Endpoint, shared_ptr<UdpFace> > ChannelFaceMap;
+ ChannelFaceMap m_channelFaces;
+
+ /**
+ * \brief If true, it means the function listen has already been called
+ */
+ bool m_isListening;
+
+};
+
+} // namespace nfd
+
+#endif // NFD_FACE_UDP_CHANNEL_HPP
diff --git a/daemon/face/udp-face.cpp b/daemon/face/udp-face.cpp
new file mode 100644
index 0000000..0ee40cc
--- /dev/null
+++ b/daemon/face/udp-face.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "udp-face.hpp"
+
+namespace nfd {
+
+NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
+
+UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket)
+ : DatagramFace<protocol>(socket)
+{
+}
+
+void
+UdpFace::handleFirstReceive(const uint8_t* buffer,
+ std::size_t nBytesReceived,
+ const boost::system::error_code& error)
+{
+ //checking if the received message size is too big.
+ //This check is redundant, since in the actual implementation a packet
+ //cannot be bigger than MAX_NDN_PACKET_SIZE
+ if (!error && (nBytesReceived > MAX_NDN_PACKET_SIZE))
+ {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received message too big. Maximum size is "
+ << MAX_NDN_PACKET_SIZE );
+ return;
+ }
+ receiveDatagram(buffer, nBytesReceived, error);
+}
+
+
+} // namespace nfd
diff --git a/daemon/face/udp-face.hpp b/daemon/face/udp-face.hpp
new file mode 100644
index 0000000..126419f
--- /dev/null
+++ b/daemon/face/udp-face.hpp
@@ -0,0 +1,40 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_UDP_FACE_HPP
+#define NFD_FACE_UDP_FACE_HPP
+
+#include "datagram-face.hpp"
+
+namespace nfd
+{
+
+/**
+ * \brief Implementation of Face abstraction that uses UDP
+ * as underlying transport mechanism
+ */
+class UdpFace : public DatagramFace<boost::asio::ip::udp>
+{
+public:
+ typedef boost::asio::ip::udp protocol;
+
+ explicit
+ UdpFace(const shared_ptr<protocol::socket>& socket);
+
+ //@todo if needed by other datagramFaces, it could be moved to datagram-face.hpp
+ /**
+ * \brief Manages the first datagram received by the UdpChannel socket set on listening
+ */
+ void
+ handleFirstReceive(const uint8_t* buffer,
+ std::size_t nBytesReceived,
+ const boost::system::error_code& error);
+
+};
+
+} // namespace nfd
+
+#endif // NFD_FACE_UDP_FACE_HPP
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
new file mode 100644
index 0000000..8dafb57
--- /dev/null
+++ b/daemon/face/udp-factory.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "udp-factory.hpp"
+#include "core/global-io.hpp"
+#include "core/resolver.hpp"
+
+namespace nfd {
+
+using namespace boost::asio;
+
+NFD_LOG_INIT("UdpFactory");
+
+UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
+ : m_defaultPort(defaultPort)
+{
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::createChannel(const udp::Endpoint& endpoint,
+ const time::Duration& timeout)
+{
+ NFD_LOG_DEBUG("Creating unicast " << endpoint);
+
+ shared_ptr<UdpChannel> channel = findChannel(endpoint);
+ if (static_cast<bool>(channel))
+ return channel;
+
+
+ //checking if the endpoint is already in use for multicast face
+ shared_ptr<MulticastUdpFace> multicast = findMulticastFace(endpoint);
+ if (static_cast<bool>(multicast))
+ throw Error("Cannot create the requested UDP unicast channel, local "
+ "endpoint is already allocated for a UDP multicast face");
+
+ if (endpoint.address().is_multicast()) {
+ throw Error("This method is only for unicast channel. The provided "
+ "endpoint is multicast. Use createMulticastFace to "
+ "create a multicast face");
+ }
+
+ channel = make_shared<UdpChannel>(boost::cref(endpoint),
+ timeout);
+ m_channels[endpoint] = channel;
+
+ return channel;
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::createChannel(const std::string& localHost,
+ const std::string& localPort,
+ const time::Duration& timeout)
+{
+ return createChannel(UdpResolver::syncResolve(localHost, localPort),
+ timeout);
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
+ const udp::Endpoint& multicastEndpoint)
+{
+ //checking if the local and musticast endpoint are already in use for a multicast face
+ shared_ptr<MulticastUdpFace> multicastFace = findMulticastFace(localEndpoint);
+ if (static_cast<bool>(multicastFace)) {
+ if (multicastFace->getMulticastGroup() == multicastEndpoint)
+ return multicastFace;
+ else
+ throw Error("Cannot create the requested UDP multicast face, local "
+ "endpoint is already allocated for a UDP multicast face "
+ "on a different multicast group");
+ }
+
+ //checking if the local endpoint is already in use for an unicast channel
+ shared_ptr<UdpChannel> unicast = findChannel(localEndpoint);
+ if (static_cast<bool>(unicast)) {
+ throw Error("Cannot create the requested UDP multicast face, local "
+ "endpoint is already allocated for a UDP unicast channel");
+ }
+
+ if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
+ throw Error("IPv6 multicast is not supported yet. Please provide an IPv4 address");
+ }
+
+ if (localEndpoint.port() != multicastEndpoint.port()) {
+ throw Error("Cannot create the requested UDP multicast face, "
+ "both endpoints should have the same port number. ");
+ }
+
+ if (!multicastEndpoint.address().is_multicast()) {
+ throw Error("Cannot create the requested UDP multicast face, "
+ "the multicast group given as input is not a multicast address");
+ }
+
+ shared_ptr<ip::udp::socket> clientSocket =
+ make_shared<ip::udp::socket>(boost::ref(getGlobalIoService()));
+
+ clientSocket->open(multicastEndpoint.protocol());
+
+ clientSocket->set_option(ip::udp::socket::reuse_address(true));
+
+ try {
+ clientSocket->bind(multicastEndpoint);
+
+ if (localEndpoint.address() != ip::address::from_string("0.0.0.0")) {
+ clientSocket->set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
+ }
+ clientSocket->set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
+ localEndpoint.address().to_v4()));
+ }
+ catch (boost::system::system_error& e) {
+ std::stringstream msg;
+ msg << "Failed to properly configure the socket, check the address (" << e.what() << ")";
+ throw Error(msg.str());
+ }
+
+ clientSocket->set_option(ip::multicast::enable_loopback(false));
+
+ multicastFace = make_shared<MulticastUdpFace>(boost::cref(clientSocket));
+ multicastFace->onFail += bind(&UdpFactory::afterFaceFailed, this, localEndpoint);
+
+ m_multicastFaces[localEndpoint] = multicastFace;
+
+ return multicastFace;
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::createMulticastFace(const std::string& localIp,
+ const std::string& multicastIp,
+ const std::string& multicastPort)
+{
+
+ return createMulticastFace(UdpResolver::syncResolve(localIp,
+ multicastPort),
+ UdpResolver::syncResolve(multicastIp,
+ multicastPort));
+}
+
+void
+UdpFactory::createFace(const FaceUri& uri,
+ const FaceCreatedCallback& onCreated,
+ const FaceConnectFailedCallback& onConnectFailed)
+{
+ resolver::AddressSelector addressSelector = resolver::AnyAddress();
+ if (uri.getScheme() == "udp4")
+ addressSelector = resolver::Ipv4Address();
+ else if (uri.getScheme() == "udp6")
+ addressSelector = resolver::Ipv6Address();
+
+ UdpResolver::asyncResolve(uri.getDomain(),
+ uri.getPort().empty() ? m_defaultPort : uri.getPort(),
+ bind(&UdpFactory::continueCreateFaceAfterResolve, this, _1,
+ onCreated, onConnectFailed),
+ onConnectFailed,
+ addressSelector);
+
+}
+
+void
+UdpFactory::continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
+ const FaceCreatedCallback& onCreated,
+ const FaceConnectFailedCallback& onConnectFailed)
+{
+ if (endpoint.address().is_multicast()) {
+ onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
+ return;
+ }
+
+ // very simple logic for now
+
+ for (ChannelMap::iterator channel = m_channels.begin();
+ channel != m_channels.end();
+ ++channel)
+ {
+ if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
+ (channel->first.address().is_v6() && endpoint.address().is_v6()))
+ {
+ channel->second->connect(endpoint, onCreated);
+ return;
+ }
+ }
+ onConnectFailed("No channels available to connect to "
+ + boost::lexical_cast<std::string>(endpoint));
+}
+
+shared_ptr<UdpChannel>
+UdpFactory::findChannel(const udp::Endpoint& localEndpoint)
+{
+ ChannelMap::iterator i = m_channels.find(localEndpoint);
+ if (i != m_channels.end())
+ return i->second;
+ else
+ return shared_ptr<UdpChannel>();
+}
+
+shared_ptr<MulticastUdpFace>
+UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
+{
+ MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
+ if (i != m_multicastFaces.end())
+ return i->second;
+ else
+ return shared_ptr<MulticastUdpFace>();
+}
+
+void
+UdpFactory::afterFaceFailed(udp::Endpoint& endpoint)
+{
+ NFD_LOG_DEBUG("afterFaceFailed: " << endpoint);
+ m_multicastFaces.erase(endpoint);
+}
+
+
+} // namespace nfd
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
new file mode 100644
index 0000000..695d1e5
--- /dev/null
+++ b/daemon/face/udp-factory.hpp
@@ -0,0 +1,171 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_UDP_FACTORY_HPP
+#define NFD_FACE_UDP_FACTORY_HPP
+
+#include "protocol-factory.hpp"
+#include "udp-channel.hpp"
+#include "multicast-udp-face.hpp"
+
+
+namespace nfd {
+
+// @todo The multicast support for ipv6 must be implemented
+
+class UdpFactory : public ProtocolFactory
+{
+public:
+ /**
+ * \brief Exception of UdpFactory
+ */
+ struct Error : public ProtocolFactory::Error
+ {
+ Error(const std::string& what) : ProtocolFactory::Error(what) {}
+ };
+
+ explicit
+ UdpFactory(const std::string& defaultPort = "6363");
+
+ /**
+ * \brief Create UDP-based channel using udp::Endpoint
+ *
+ * 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 a multicast face is already active on the same local endpoint,
+ * 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
+ */
+ shared_ptr<UdpChannel>
+ createChannel(const udp::Endpoint& localEndpoint,
+ const time::Duration& timeout = time::seconds(600));
+
+ /**
+ * \brief Create UDP-based channel using specified host and port number
+ *
+ * This method will attempt to resolve the provided host and port numbers
+ * and will throw UdpFactory::Error when channel cannot be created.
+ *
+ * Note that this call will **BLOCK** until resolution is done or failed.
+ *
+ * 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 ::
+ *
+ * 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
+ *
+ * \throws UdpChannel::Error if the bind on the socket fails
+ * \throws UdpFactory::Error
+ */
+ shared_ptr<UdpChannel>
+ createChannel(const std::string& localHost,
+ const std::string& localPort,
+ const time::Duration& timeout = time::seconds(600));
+
+ /**
+ * \brief Create MulticastUdpFace using udp::Endpoint
+ *
+ * udp::Endpoint is really an alias for boost::asio::ip::udp::endpoint.
+ *
+ * The face will join the 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 an unicast face is already active on the same local NIC and port, the
+ * creation fails and an exception is thrown
+ *
+ * \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
+ */
+ shared_ptr<MulticastUdpFace>
+ createMulticastFace(const udp::Endpoint& localEndpoint,
+ const udp::Endpoint& multicastEndpoint);
+
+ shared_ptr<MulticastUdpFace>
+ createMulticastFace(const std::string& localIp,
+ const std::string& multicastIp,
+ const std::string& multicastPort);
+
+ // from Factory
+ virtual void
+ createFace(const FaceUri& uri,
+ const FaceCreatedCallback& onCreated,
+ const FaceConnectFailedCallback& onConnectFailed);
+
+protected:
+ typedef std::map< udp::Endpoint, shared_ptr<MulticastUdpFace> > MulticastFaceMap;
+
+ /**
+ * \brief Keeps tracking of the MulticastUdpFace created
+ */
+ MulticastFaceMap m_multicastFaces;
+
+private:
+ void
+ afterFaceFailed(udp::Endpoint& endpoint);
+
+ /**
+ * \brief Look up UdpChannel using specified local endpoint
+ *
+ * \returns shared pointer to the existing UdpChannel object
+ * or empty shared pointer when such channel does not exist
+ *
+ * \throws never
+ */
+ shared_ptr<UdpChannel>
+ findChannel(const udp::Endpoint& localEndpoint);
+
+
+ /**
+ * \brief Look up multicast UdpFace using specified local endpoint
+ *
+ * \returns shared pointer to the existing multicast MulticastUdpFace object
+ * or empty shared pointer when such face does not exist
+ *
+ * \throws never
+ */
+ shared_ptr<MulticastUdpFace>
+ findMulticastFace(const udp::Endpoint& localEndpoint);
+
+ void
+ continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
+ const FaceCreatedCallback& onCreated,
+ const FaceConnectFailedCallback& onConnectFailed);
+
+ typedef std::map< udp::Endpoint, shared_ptr<UdpChannel> > ChannelMap;
+ ChannelMap m_channels;
+
+ std::string m_defaultPort;
+};
+
+} // namespace nfd
+
+#endif // NFD_FACE_UDP_FACTORY_HPP