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;
   };