face: configuration option to make UDP multicast face "ad hoc"

Change-Id: Ifd407f42e9646826697f73ab1890c819fce16857
Refs: #4018, #3967
diff --git a/daemon/face/multicast-udp-transport.cpp b/daemon/face/multicast-udp-transport.cpp
index 34bef0c..7b3fca3 100644
--- a/daemon/face/multicast-udp-transport.cpp
+++ b/daemon/face/multicast-udp-transport.cpp
@@ -35,7 +35,8 @@
 MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndpoint,
                                              const protocol::endpoint& multicastGroup,
                                              protocol::socket&& recvSocket,
-                                             protocol::socket&& sendSocket)
+                                             protocol::socket&& sendSocket,
+                                             ndn::nfd::LinkType linkType)
   : DatagramTransport(std::move(recvSocket))
   , m_multicastGroup(multicastGroup)
   , m_sendSocket(std::move(sendSocket))
@@ -44,7 +45,7 @@
   this->setRemoteUri(FaceUri(multicastGroup));
   this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
   this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
-  this->setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+  this->setLinkType(linkType);
   this->setMtu(udp::computeMtu(localEndpoint));
 
   NFD_LOG_FACE_INFO("Creating transport");
diff --git a/daemon/face/multicast-udp-transport.hpp b/daemon/face/multicast-udp-transport.hpp
index 2b04eb7..9b903d0 100644
--- a/daemon/face/multicast-udp-transport.hpp
+++ b/daemon/face/multicast-udp-transport.hpp
@@ -51,11 +51,13 @@
    * \param multicastGroup multicast group
    * \param recvSocket socket used to receive packets
    * \param sendSocket socket used to send to the multicast group
+   * \param linkType either LINK_TYPE_MULTI_ACCESS or LINK_TYPE_AD_HOC
    */
   MulticastUdpTransport(const protocol::endpoint& localEndpoint,
                         const protocol::endpoint& multicastGroup,
                         protocol::socket&& recvSocket,
-                        protocol::socket&& sendSocket);
+                        protocol::socket&& sendSocket,
+                        ndn::nfd::LinkType linkType);
 
 private:
   void
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 8ea503c..d141e58 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -66,6 +66,7 @@
   //   mcast yes
   //   mcast_group 224.0.23.170
   //   mcast_port 56363
+  //   mcast_ad_hoc no
   //   whitelist
   //   {
   //     *
@@ -123,6 +124,10 @@
       else if (key == "mcast_port") {
         mcastConfig.group.port(ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp"));
       }
+      else if (key == "mcast_ad_hoc") {
+        bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.udp");
+        mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
+      }
       else if (key == "whitelist") {
         mcastConfig.netifPredicate.parseWhitelist(value);
       }
@@ -176,18 +181,21 @@
         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 if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
-      NFD_LOG_INFO("changing whitelist/blacklist");
-    }
-    else {
-      // There's no configuration change, but we still need to re-apply configuration because
-      // netifs may have changed.
+    else if (mcastConfig.isEnabled) {
+      if (m_mcastConfig.linkType != mcastConfig.linkType && !m_mcastFaces.empty()) {
+        NFD_LOG_WARN("Cannot change ad hoc setting on existing faces");
+      }
+      if (m_mcastConfig.group != mcastConfig.group) {
+        NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
+                     " to " << mcastConfig.group);
+      }
+      if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
+        NFD_LOG_INFO("changing whitelist/blacklist");
+      }
     }
 
+    // Even if there's no configuration change, we still need to re-apply configuration because
+    // netifs may have changed.
     m_mcastConfig = mcastConfig;
     this->applyMulticastConfig(context);
   }
@@ -427,7 +435,8 @@
   auto linkService = make_unique<GenericLinkService>();
   auto transport = make_unique<MulticastUdpTransport>(localEndpoint, multicastEndpoint,
                                                       std::move(receiveSocket),
-                                                      std::move(sendSocket));
+                                                      std::move(sendSocket),
+                                                      m_mcastConfig.linkType);
   face = make_shared<Face>(std::move(linkService), std::move(transport));
 
   m_mcastFaces[localEndpoint] = face;
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 9b68448..e662e6a 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -199,6 +199,7 @@
   {
     bool isEnabled = false;
     udp::Endpoint group = udp::getDefaultMulticastGroup();
+    ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS;
     NetworkInterfacePredicate netifPredicate;
   };
 
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index fe3efef..8d6f5d1 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -125,8 +125,9 @@
     ;    sudo setcap cap_net_raw=eip /full/path/nfd
     ;
     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)
+    mcast_port 56363 ; UDP multicast port number
+    mcast_ad_hoc no ; set 'yes' to make all UDP multicast faces "ad hoc", default 'no'
 
     ; Whitelist and blacklist can contain, in no particular order:
     ; interface names, including wildcard patterns (e.g., 'ifname eth0', 'ifname en*', 'ifname wlp?s0'),
diff --git a/tests/daemon/face/multicast-udp-transport-fixture.hpp b/tests/daemon/face/multicast-udp-transport-fixture.hpp
index b8ed524..574cb58 100644
--- a/tests/daemon/face/multicast-udp-transport-fixture.hpp
+++ b/tests/daemon/face/multicast-udp-transport-fixture.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,
@@ -63,10 +63,12 @@
     udp::socket sockTx(g_io);
     localEp = udp::endpoint(address, 7001);
     openMulticastSockets(sockRx, sockTx, localEp.port());
+    ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS;
 
     face = make_unique<Face>(
              make_unique<DummyReceiveLinkService>(),
-             make_unique<MulticastUdpTransport>(localEp, multicastEp, std::move(sockRx), std::move(sockTx)));
+             make_unique<MulticastUdpTransport>(localEp, multicastEp, std::move(sockRx),
+                                                std::move(sockTx), linkType));
     transport = static_cast<MulticastUdpTransport*>(face->getTransport());
     receivedPackets = &static_cast<DummyReceiveLinkService*>(face->getLinkService())->receivedPackets;
 
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index 265bf97..14a0aee 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -124,15 +124,15 @@
   }
 
   std::vector<const Face*>
-  listUdpMcastFaces() const
+  listUdpMcastFaces(ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS) const
   {
-    return this->listFacesByScheme("udp4", ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+    return this->listFacesByScheme("udp4", linkType);
   }
 
   size_t
-  countUdpMcastFaces() const
+  countUdpMcastFaces(ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS) const
   {
-    return this->listUdpMcastFaces().size();
+    return this->listUdpMcastFaces(linkType).size();
   }
 
   /** \brief determine whether a UDP multicast face is created on \p netif
@@ -199,6 +199,28 @@
   BOOST_CHECK_EQUAL(this->countUdpMcastFaces(), 0);
 }
 
+BOOST_FIXTURE_TEST_CASE(McastAdHoc, 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 CONFIG = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        mcast_ad_hoc yes
+      }
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, false);
+  BOOST_CHECK_EQUAL(this->countUdpMcastFaces(ndn::nfd::LINK_TYPE_AD_HOC), netifs.size());
+}
+
 BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpoint, UdpMcastConfigFixture)
 {
 #ifdef __linux__