rib: error out if both localhop_security and auto_prefix_propagate are enabled

Refs: #4989
Change-Id: Ib742880736a8f5c25e2531d2669a8b993f9f1cd6
diff --git a/daemon/rib/service.cpp b/daemon/rib/service.cpp
index 564e3d6..72045bf 100644
--- a/daemon/rib/service.cpp
+++ b/daemon/rib/service.cpp
@@ -45,14 +45,14 @@
 
 Service* Service::s_instance = nullptr;
 
-static const std::string CFG_SECTION = "rib";
-static const std::string CFG_LOCALHOST_SECURITY = "localhost_security";
-static const std::string CFG_LOCALHOP_SECURITY = "localhop_security";
-static const std::string CFG_PREFIX_PROPAGATE = "auto_prefix_propagate";
-static const std::string CFG_READVERTISE_NLSR = "readvertise_nlsr";
-static const Name READVERTISE_NLSR_PREFIX = "/localhost/nlsr";
-static const uint64_t PROPAGATE_DEFAULT_COST = 15;
-static const time::milliseconds PROPAGATE_DEFAULT_TIMEOUT = 10_s;
+const std::string CFG_SECTION = "rib";
+const std::string CFG_LOCALHOST_SECURITY = "localhost_security";
+const std::string CFG_LOCALHOP_SECURITY = "localhop_security";
+const std::string CFG_PREFIX_PROPAGATE = "auto_prefix_propagate";
+const std::string CFG_READVERTISE_NLSR = "readvertise_nlsr";
+const Name READVERTISE_NLSR_PREFIX = "/localhost/nlsr";
+const uint64_t PROPAGATE_DEFAULT_COST = 15;
+const time::milliseconds PROPAGATE_DEFAULT_TIMEOUT = 10_s;
 
 static ConfigSection
 loadConfigSectionFromFile(const std::string& filename)
@@ -160,14 +160,19 @@
 void
 Service::checkConfig(const ConfigSection& section, const std::string& filename)
 {
+  bool hasLocalhop = false;
+  bool hasPropagate = false;
+
   for (const auto& item : section) {
     const std::string& key = item.first;
     const ConfigSection& value = item.second;
     if (key == CFG_LOCALHOST_SECURITY || key == CFG_LOCALHOP_SECURITY) {
+      hasLocalhop = key == CFG_LOCALHOP_SECURITY;
       ndn::ValidatorConfig testValidator(m_face);
       testValidator.load(value, filename);
     }
     else if (key == CFG_PREFIX_PROPAGATE) {
+      hasPropagate = true;
       // AutoPrefixPropagator does not support config dry-run
     }
     else if (key == CFG_READVERTISE_NLSR) {
@@ -177,6 +182,11 @@
       NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_SECTION + "." + key));
     }
   }
+
+  if (hasLocalhop && hasPropagate) {
+    NDN_THROW(ConfigFile::Error(CFG_LOCALHOP_SECURITY + " and " + CFG_PREFIX_PROPAGATE +
+                                " cannot be enabled at the same time"));
+  }
 }
 
 void
@@ -200,21 +210,15 @@
       if (!m_readvertisePropagation) {
         NFD_LOG_DEBUG("Enabling automatic prefix propagation");
 
-        auto parameters = ndn::nfd::ControlParameters()
-          .setCost(PROPAGATE_DEFAULT_COST)
-          .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT);
         auto cost = item.second.get_optional<uint64_t>("cost");
-        if (cost) {
-          parameters.setCost(*cost);
-        }
+        auto parameters = ndn::nfd::ControlParameters()
+                          .setCost(cost.value_or(PROPAGATE_DEFAULT_COST))
+                          .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT);
 
-        auto options = ndn::nfd::CommandOptions()
-          .setPrefix(RibManager::LOCALHOP_TOP_PREFIX)
-          .setTimeout(PROPAGATE_DEFAULT_TIMEOUT);
         auto timeout = item.second.get_optional<uint64_t>("timeout");
-        if (timeout) {
-          options.setTimeout(time::milliseconds(*timeout));
-        }
+        auto options = ndn::nfd::CommandOptions()
+                       .setPrefix(RibManager::LOCALHOP_TOP_PREFIX)
+                       .setTimeout(timeout ? time::milliseconds(*timeout) : PROPAGATE_DEFAULT_TIMEOUT);
 
         m_readvertisePropagation = make_unique<Readvertise>(
           m_rib,
diff --git a/tests/daemon/rib/service.t.cpp b/tests/daemon/rib/service.t.cpp
index 80b0c38..4832afd 100644
--- a/tests/daemon/rib/service.t.cpp
+++ b/tests/daemon/rib/service.t.cpp
@@ -29,37 +29,93 @@
 #include "tests/test-common.hpp"
 #include "tests/daemon/rib-io-fixture.hpp"
 
+#include <boost/property_tree/info_parser.hpp>
+#include <sstream>
+
 namespace nfd {
 namespace rib {
 namespace tests {
 
 using namespace nfd::tests;
 
-BOOST_FIXTURE_TEST_SUITE(TestService, RibIoFixture)
+class RibServiceFixture : public RibIoFixture
+{
+protected:
+  static ConfigSection
+  makeSection(const std::string& text, bool wantUnixSocketPath = true)
+  {
+    std::istringstream is(text);
+    ConfigSection section;
+    boost::property_tree::read_info(is, section);
+    if (wantUnixSocketPath)
+      section.put("face_system.unix.path", "/dev/null");
+    return section;
+  }
+
+protected:
+  ndn::KeyChain m_ribKeyChain;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestService, RibServiceFixture)
 
 BOOST_AUTO_TEST_CASE(Basic)
 {
-  ConfigSection section;
-  section.put("face_system.unix.path", "/var/run/nfd.sock");
-
-  ndn::KeyChain ribKeyChain;
+  auto section = makeSection("");
 
   BOOST_CHECK_THROW(Service::get(), std::logic_error);
-  BOOST_CHECK_THROW(Service(section, ribKeyChain), std::logic_error);
+  BOOST_CHECK_THROW(Service(section, m_ribKeyChain), std::logic_error);
 
   runOnRibIoService([&] {
     {
       BOOST_CHECK_THROW(Service::get(), std::logic_error);
-      Service ribService(section, ribKeyChain);
+      Service ribService(section, m_ribKeyChain);
       BOOST_CHECK_EQUAL(&ribService, &Service::get());
     }
     BOOST_CHECK_THROW(Service::get(), std::logic_error);
-    Service ribService(section, ribKeyChain);
-    BOOST_CHECK_THROW(Service(section, ribKeyChain), std::logic_error);
+    Service ribService(section, m_ribKeyChain);
+    BOOST_CHECK_THROW(Service(section, m_ribKeyChain), std::logic_error);
   });
+  poll();
 }
 
-BOOST_AUTO_TEST_SUITE_END() // TestRibService
+BOOST_AUTO_TEST_SUITE(ProcessConfig)
+
+BOOST_AUTO_TEST_CASE(LocalhopAndPropagate)
+{
+  const std::string CONFIG = R"CONFIG(
+    rib
+    {
+      localhost_security
+      {
+        trust-anchor
+        {
+          type any
+        }
+      }
+      localhop_security
+      {
+        trust-anchor
+        {
+          type any
+        }
+      }
+      auto_prefix_propagate
+    }
+  )CONFIG";
+
+  runOnRibIoService([&] {
+    BOOST_CHECK_EXCEPTION(Service(makeSection(CONFIG), m_ribKeyChain), ConfigFile::Error,
+                          [] (const auto& e) {
+                            return e.what() == "localhop_security and auto_prefix_propagate "
+                                               "cannot be enabled at the same time"s;
+                          });
+  });
+  poll();
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
+
+BOOST_AUTO_TEST_SUITE_END() // TestService
 
 } // namespace tests
 } // namespace rib