face: Connect faces to the parent (unicast) or associated (multicast) channels

Change-Id: Ie7b2e8fb60050a2bc6e4f5ad053c26e121f3c3d5
Refs: #4973
diff --git a/daemon/face/channel.hpp b/daemon/face/channel.hpp
index 4b4b8c6..ffd06d7 100644
--- a/daemon/face/channel.hpp
+++ b/daemon/face/channel.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -38,7 +38,7 @@
  *  A channel can listen on a local endpoint and initiate outgoing connection from a local endpoint.
  *  A channel creates Face objects and retains shared ownership of them.
  */
-class Channel : noncopyable
+class Channel : public std::enable_shared_from_this<Channel>, noncopyable
 {
 public:
   virtual
diff --git a/daemon/face/ethernet-channel.cpp b/daemon/face/ethernet-channel.cpp
index 725bbfd..512d078 100644
--- a/daemon/face/ethernet-channel.cpp
+++ b/daemon/face/ethernet-channel.cpp
@@ -209,6 +209,7 @@
   auto transport = make_unique<UnicastEthernetTransport>(*m_localEndpoint, remoteEndpoint,
                                                          params.persistency, m_idleFaceTimeout);
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
+  face->setChannel(shared_from_this()); // use weak_from_this() in C++17
 
   m_channelFaces[remoteEndpoint] = face;
   connectFaceClosedSignal(*face, [this, remoteEndpoint] {
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 9659985..fec671f 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -257,6 +257,9 @@
   m_mcastFaces[key] = face;
   connectFaceClosedSignal(*face, [this, key] { m_mcastFaces.erase(key); });
 
+  auto channelIt = m_channels.find(netif.getName());
+  face->setChannel(channelIt != m_channels.end() ? channelIt->second : nullptr);
+
   return face;
 }
 
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 4a5948b..1bb474e 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -34,6 +34,8 @@
 namespace nfd {
 namespace face {
 
+class Channel;
+
 /** \brief indicates the state of a face
  */
 typedef TransportState FaceState;
@@ -171,11 +173,30 @@
   const FaceCounters&
   getCounters() const;
 
+  /**
+   * \brief Get channel on which face was created (unicast) or the associated channel (multicast)
+   */
+  weak_ptr<Channel>
+  getChannel() const
+  {
+    return m_channel;
+  }
+
+  /**
+   * \brief Set channel on which face was created (unicast) or the associated channel (multicast)
+   */
+  void
+  setChannel(weak_ptr<Channel> channel)
+  {
+    m_channel = std::move(channel);
+  }
+
 private:
   FaceId m_id;
   unique_ptr<LinkService> m_service;
   unique_ptr<Transport> m_transport;
   FaceCounters m_counters;
+  weak_ptr<Channel> m_channel;
 };
 
 inline LinkService*
diff --git a/daemon/face/tcp-channel.cpp b/daemon/face/tcp-channel.cpp
index cfbaed6..6cf50c4 100644
--- a/daemon/face/tcp-channel.cpp
+++ b/daemon/face/tcp-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -129,6 +129,7 @@
                                           socket.remote_endpoint().address());
     auto transport = make_unique<TcpTransport>(std::move(socket), params.persistency, faceScope);
     face = make_shared<Face>(std::move(linkService), std::move(transport));
+    face->setChannel(shared_from_this()); // use weak_from_this() in C++17
 
     m_channelFaces[remoteEndpoint] = face;
     connectFaceClosedSignal(*face, [this, remoteEndpoint] { m_channelFaces.erase(remoteEndpoint); });
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index 4776661..f2ccb9c 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -188,6 +188,7 @@
   auto transport = make_unique<UnicastUdpTransport>(std::move(socket), params.persistency,
                                                     m_idleFaceTimeout);
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
+  face->setChannel(shared_from_this()); // use weak_from_this() in C++17
 
   m_channelFaces[remoteEndpoint] = face;
   connectFaceClosedSignal(*face, [this, remoteEndpoint] { m_channelFaces.erase(remoteEndpoint); });
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index b15b762..993f61a 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -362,6 +362,13 @@
   m_mcastFaces[localEp] = face;
   connectFaceClosedSignal(*face, [this, localEp] { m_mcastFaces.erase(localEp); });
 
+  // Associate with the first available channel of the same protocol family
+  auto channelIt = std::find_if(m_channels.begin(), m_channels.end(),
+                                [isV4 = localEp.address().is_v4()] (const auto& it) {
+                                  return it.first.address().is_v4() == isV4;
+                                });
+  face->setChannel(channelIt != m_channels.end() ? channelIt->second : nullptr);
+
   return face;
 }
 
diff --git a/daemon/face/unix-stream-channel.cpp b/daemon/face/unix-stream-channel.cpp
index b60ba78..760a264 100644
--- a/daemon/face/unix-stream-channel.cpp
+++ b/daemon/face/unix-stream-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -137,6 +137,7 @@
   auto linkService = make_unique<GenericLinkService>(options);
   auto transport = make_unique<UnixStreamTransport>(std::move(m_socket));
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
+  face->setChannel(shared_from_this()); // use weak_from_this() in C++17
 
   ++m_size;
   connectFaceClosedSignal(*face, [this] { --m_size; });
diff --git a/daemon/face/websocket-channel.cpp b/daemon/face/websocket-channel.cpp
index a2660c6..13f6f38 100644
--- a/daemon/face/websocket-channel.cpp
+++ b/daemon/face/websocket-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -126,6 +126,7 @@
   auto linkService = make_unique<GenericLinkService>();
   auto transport = make_unique<WebSocketTransport>(hdl, m_server, m_pingInterval);
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
+  face->setChannel(shared_from_this()); // use weak_from_this() in C++17
 
   BOOST_ASSERT(m_channelFaces.count(hdl) == 0);
   m_channelFaces[hdl] = face;
diff --git a/tests/daemon/face/channel-fixture.hpp b/tests/daemon/face/channel-fixture.hpp
index 2ec7d8a..58b5ee4 100644
--- a/tests/daemon/face/channel-fixture.hpp
+++ b/tests/daemon/face/channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -63,14 +63,14 @@
     return m_nextPort++;
   }
 
-  virtual unique_ptr<ChannelT>
+  virtual shared_ptr<ChannelT>
   makeChannel()
   {
     BOOST_FAIL("Unimplemented");
     return nullptr;
   }
 
-  virtual unique_ptr<ChannelT>
+  virtual shared_ptr<ChannelT>
   makeChannel(const boost::asio::ip::address&, uint16_t port = 0)
   {
     BOOST_FAIL("Unimplemented");
@@ -101,7 +101,7 @@
 protected:
   LimitedIo limitedIo;
   EndpointT listenerEp;
-  unique_ptr<ChannelT> listenerChannel;
+  shared_ptr<ChannelT> listenerChannel;
   std::vector<shared_ptr<Face>> listenerFaces;
 
 private:
diff --git a/tests/daemon/face/ethernet-channel.t.cpp b/tests/daemon/face/ethernet-channel.t.cpp
index 950bdd2..753f36a 100644
--- a/tests/daemon/face/ethernet-channel.t.cpp
+++ b/tests/daemon/face/ethernet-channel.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -36,11 +36,11 @@
 class EthernetChannelFixture : public EthernetFixture
 {
 protected:
-  unique_ptr<EthernetChannel>
+  shared_ptr<EthernetChannel>
   makeChannel()
   {
     BOOST_ASSERT(netifs.size() > 0);
-    return make_unique<EthernetChannel>(netifs.front(), 2_s);
+    return std::make_shared<EthernetChannel>(netifs.front(), 2_s);
   }
 };
 
@@ -89,6 +89,8 @@
   BOOST_CHECK_EQUAL(channel->size(), 1);
   BOOST_REQUIRE(face != nullptr);
 
+  BOOST_CHECK_EQUAL(face->getChannel().lock(), channel);
+
   face->close();
   g_io.poll();
   BOOST_CHECK_EQUAL(channel->size(), 0);
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 605d3e2..269c79f 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -151,6 +151,11 @@
   parseConfig(CONFIG, false);
 
   BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), netifs.size());
+  for (const auto& face : this->listEtherMcastFaces()) {
+    BOOST_REQUIRE(face->getChannel().lock());
+    // not universal, but for Ethernet, local URI of a mcast face matches URI of the associated channel
+    BOOST_CHECK_EQUAL(face->getLocalUri(), face->getChannel().lock()->getUri());
+  }
 }
 
 BOOST_AUTO_TEST_CASE(EnableDisableMcast)
diff --git a/tests/daemon/face/tcp-channel-fixture.hpp b/tests/daemon/face/tcp-channel-fixture.hpp
index b872f94..41f17d5 100644
--- a/tests/daemon/face/tcp-channel-fixture.hpp
+++ b/tests/daemon/face/tcp-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -43,14 +43,14 @@
     local.assign({{"subnet", "127.0.0.0/8"}, {"subnet", "::1/128"}}, {});
   }
 
-  unique_ptr<TcpChannel>
+  shared_ptr<TcpChannel>
   makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
   {
     if (port == 0)
       port = getNextPort();
 
-    return make_unique<TcpChannel>(tcp::Endpoint(addr, port), false,
-                                   std::bind(&TcpChannelFixture::determineFaceScope, this, _1, _2));
+    return std::make_shared<TcpChannel>(tcp::Endpoint(addr, port), false,
+                                        std::bind(&TcpChannelFixture::determineFaceScope, this, _1, _2));
   }
 
   void
diff --git a/tests/daemon/face/tcp-udp-channel.t.cpp b/tests/daemon/face/tcp-udp-channel.t.cpp
index 5b86e9d..020c954 100644
--- a/tests/daemon/face/tcp-udp-channel.t.cpp
+++ b/tests/daemon/face/tcp-udp-channel.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -137,6 +137,8 @@
   BOOST_CHECK_EQUAL(this->listenerChannel->size(), 1);
   BOOST_CHECK_EQUAL(clientChannel->size(), 1);
 
+  BOOST_CHECK_EQUAL(this->clientFaces.at(0)->getChannel().lock(), clientChannel);
+
   this->clientFaces.at(0)->close();
 
   BOOST_CHECK_EQUAL(this->limitedIo.run(2, 5_s), LimitedIo::EXCEED_OPS);
diff --git a/tests/daemon/face/udp-channel-fixture.hpp b/tests/daemon/face/udp-channel-fixture.hpp
index 5192a97..93c5a86 100644
--- a/tests/daemon/face/udp-channel-fixture.hpp
+++ b/tests/daemon/face/udp-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -38,13 +38,13 @@
 class UdpChannelFixture : public ChannelFixture<UdpChannel, udp::Endpoint>
 {
 protected:
-  unique_ptr<UdpChannel>
+  shared_ptr<UdpChannel>
   makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
   {
     if (port == 0)
       port = getNextPort();
 
-    return make_unique<UdpChannel>(udp::Endpoint(addr, port), 2_s, false);
+    return std::make_shared<UdpChannel>(udp::Endpoint(addr, port), 2_s, false);
   }
 
   void
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index c09fe62..0bf7e03 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -300,6 +300,17 @@
   BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), netifsV4.size());
   BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), netifsV6.size());
 
+  BOOST_REQUIRE_EQUAL(factory.getChannels().size(), 2);
+  for (const auto& face : this->listUdp4McastFaces()) {
+    BOOST_REQUIRE(face->getChannel().lock());
+    BOOST_CHECK_EQUAL(face->getChannel().lock()->getUri().getScheme(), "udp4");
+  }
+
+  for (const auto& face : this->listUdp6McastFaces()) {
+    BOOST_REQUIRE(face->getChannel().lock());
+    BOOST_CHECK_EQUAL(face->getChannel().lock()->getUri().getScheme(), "udp6");
+  }
+
   parseConfig(CONFIG_WITHOUT_MCAST, false);
   g_io.poll();
   BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
diff --git a/tests/daemon/face/unix-stream-channel.t.cpp b/tests/daemon/face/unix-stream-channel.t.cpp
index fe9d4be..fa9e480 100644
--- a/tests/daemon/face/unix-stream-channel.t.cpp
+++ b/tests/daemon/face/unix-stream-channel.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -45,10 +45,10 @@
     listenerEp = unix_stream::Endpoint("nfd-test-unix-stream-channel.sock");
   }
 
-  unique_ptr<UnixStreamChannel>
+  shared_ptr<UnixStreamChannel>
   makeChannel() final
   {
-    return make_unique<UnixStreamChannel>(listenerEp, false);
+    return std::make_shared<UnixStreamChannel>(listenerEp, false);
   }
 
   void
@@ -120,9 +120,10 @@
   BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
   BOOST_CHECK_EQUAL(listenerFaces.size(), 3);
 
-  // check face persistency
+  // check face persistency and channel association
   for (const auto& face : listenerFaces) {
     BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+    BOOST_CHECK_EQUAL(face->getChannel().lock(), listenerChannel);
   }
 }
 
diff --git a/tests/daemon/face/websocket-channel-fixture.hpp b/tests/daemon/face/websocket-channel-fixture.hpp
index 5500281..f28459f 100644
--- a/tests/daemon/face/websocket-channel-fixture.hpp
+++ b/tests/daemon/face/websocket-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -37,13 +37,13 @@
 class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
 {
 protected:
-  unique_ptr<WebSocketChannel>
+  shared_ptr<WebSocketChannel>
   makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
   {
     if (port == 0)
       port = getNextPort();
 
-    return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
+    return std::make_shared<WebSocketChannel>(websocket::Endpoint(addr, port));
   }
 
   void
diff --git a/tests/daemon/face/websocket-channel.t.cpp b/tests/daemon/face/websocket-channel.t.cpp
index 0631efa..d9fe929 100644
--- a/tests/daemon/face/websocket-channel.t.cpp
+++ b/tests/daemon/face/websocket-channel.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -88,9 +88,10 @@
                                   2_s), LimitedIo::EXCEED_OPS);
   BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
 
-  // check face persistency
+  // check face persistency and channel association
   for (const auto& face : listenerFaces) {
     BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+    BOOST_CHECK_EQUAL(face->getChannel().lock(), listenerChannel);
   }
 }
 
@@ -152,6 +153,8 @@
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address);
 
+  BOOST_CHECK_EQUAL(listenerFaces.at(0)->getChannel().lock(), listenerChannel);
+
   listenerFaces.at(0)->close();
   BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
 }