tools: nfdc
Change-Id: Ida7d3b4264fd3452acf69b443150e3ad639e3da9
diff --git a/tools/nfdc.cpp b/tools/nfdc.cpp
new file mode 100644
index 0000000..1ad54af
--- /dev/null
+++ b/tools/nfdc.cpp
@@ -0,0 +1,269 @@
+/* -*- 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 "nfdc.hpp"
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/regex_find_format.hpp>
+#include <boost/regex.hpp>
+
+void
+usage(const char* programName)
+{
+ std::cout << "Usage:\n" << programName << " [-h] COMMAND\n"
+ " -h print usage and exit\n"
+ "\n"
+ " COMMAND can be one of following:\n"
+ " insert <name> \n"
+ " Insert a FIB entry \n"
+ " delete <name> \n"
+ " Delete a FIB entry\n"
+ " add-nexthop <name> <faceId> [<cost>]\n"
+ " Add a nexthop to an existing FIB entry\n"
+ " remove-nexthop <name> <faceId> \n"
+ " Remove a nexthop from a FIB entry\n"
+ " set-strategy <name> <stratgy>\n"
+ " Set a forwarding strategy for a namespace\n"
+ " create <uri> \n"
+ " Create a face in one of the following formats:\n"
+ " UDP unicast: udp[4|6]://<remote-IP-or-host>[:<remote-port>]\n"
+ " TCP: tcp[4|6]://<remote-IP-or-host>[:<remote-port>] \n"
+ " destroy <faceId> \n"
+ " Destroy a face\n"
+ << std::endl;
+}
+
+namespace nfdc {
+
+Controller::Controller(ndn::Face& face)
+ : ndn::nfd::Controller(face)
+{
+}
+
+Controller::~Controller()
+{
+}
+bool
+Controller::dispatch(const std::string& command, const char* commandOptions[], int nOptions)
+{
+ if (command == "insert") {
+ if (nOptions != 1)
+ return false;
+ fibInsert(commandOptions);
+ }
+ else if (command == "delete") {
+ if (nOptions != 1)
+ return false;
+ fibDelete(commandOptions);
+ }
+ else if (command == "add-nexthop") {
+ if (nOptions == 2)
+ fibAddNextHop(commandOptions, false);
+ else if (nOptions == 3)
+ fibAddNextHop(commandOptions, true);
+ else
+ return false;
+ }
+ else if (command == "remove-nexthop") {
+ if (nOptions != 2)
+ return false;
+ fibRemoveNextHop(commandOptions);
+ }
+ else if (command == "set-strategy") {
+ if (nOptions != 2)
+ return false;
+ fibSetStrategy(commandOptions);
+ }
+ else if (command == "create") {
+ if (nOptions != 1)
+ return false;
+ faceCreate(commandOptions);
+ }
+ else if (command == "destroy") {
+ if (nOptions != 1)
+ return false;
+ faceDestroy(commandOptions);
+ }
+ else
+ usage(m_programName);
+
+ return true;
+}
+
+void
+Controller::fibInsert(const char* commandOptions[])
+{
+ const std::string& name = commandOptions[0];
+
+ ndn::nfd::FibManagementOptions fibOptions;
+ fibOptions.setName(name);
+ startFibCommand("insert",
+ fibOptions,
+ bind(&Controller::onFibSuccess, this, _1, "Fib insertion succeeded"),
+ bind(&Controller::onError, this, _1, "Fib insertion failed"));
+}
+
+void
+Controller::fibDelete(const char* commandOptions[])
+{
+ const std::string& name = commandOptions[0];
+ ndn::nfd::FibManagementOptions fibOptions;
+ fibOptions.setName(name);
+ startFibCommand("delete",
+ fibOptions,
+ bind(&Controller::onFibSuccess, this, _1, "Fib deletion succeeded"),
+ bind(&Controller::onError, this, _1, "Fib deletion failed" ));
+}
+
+
+void
+Controller::fibAddNextHop(const char* commandOptions[], bool hasCost)
+{
+ ndn::nfd::FibManagementOptions fibOptions;
+
+ const std::string& name = commandOptions[0];
+ const int faceId = boost::lexical_cast<int>(commandOptions[1]);
+
+ fibOptions.setName(name);
+ fibOptions.setFaceId(faceId);
+
+ if (hasCost)
+ {
+ const int cost = boost::lexical_cast<int>(commandOptions[2]);
+ fibOptions.setCost(cost);
+ }
+ startFibCommand("add-nexthop",
+ fibOptions,
+ bind(&Controller::onFibSuccess, this, _1, "Nexthop insertion succeeded"),
+ bind(&Controller::onError, this, _1, "Nexthop insertion failed"));
+}
+
+void
+Controller::fibRemoveNextHop(const char* commandOptions[])
+{
+ const std::string& name = commandOptions[0];
+ const int faceId = boost::lexical_cast<int>(commandOptions[1]);
+ ndn::nfd::FibManagementOptions fibOptions;
+
+ fibOptions.setName(name);
+ fibOptions.setFaceId(faceId);
+ startFibCommand("remove-nexthop",
+ fibOptions,
+ bind(&Controller::onFibSuccess, this, _1, "Nexthop Removal succeeded"),
+ bind(&Controller::onError, this, _1, "Nexthop Removal failed"));
+}
+
+void
+Controller::fibSetStrategy(const char* commandOptions[])
+{
+
+ // std::string name = commandOptions[0];
+ // std::string strategy = commandOptions[1];
+ // startFibCommand("set-strategy",
+ // ndn::nfd::FibManagementOptions()
+ // .setName(name)
+ // .setStrategy(strategy),
+ // bind(&Controller::onFibSuccess,this, _1),
+ // bind(&Controller::onError,this, _1));
+}
+
+namespace {
+bool
+isValidUri(const std::string& input)
+{
+ // an extended regex to support the validation of uri structure
+ // boost::regex e("^[a-z0-9]+-?+[a-z0-9]+\\:\\/\\/.*");
+ boost::regex e("^[a-z0-9]+\\:.*");
+ return boost::regex_match(input, e);
+}
+} // anonymous namespace
+
+void
+Controller::faceCreate(const char* commandOptions[])
+{
+ ndn::nfd::FaceManagementOptions faceOptions;
+ const std::string& uri = commandOptions[0];
+ faceOptions.setUri(uri);
+
+ if (isValidUri(uri))
+ {
+ startFaceCommand("create",
+ faceOptions,
+ bind(&Controller::onFaceSuccess, this, _1, "Face creation succeeded"),
+ bind(&Controller::onError, this, _1, "Face creation failed"));
+ }
+ else
+ throw Error("invalid uri format");
+}
+
+void
+Controller::faceDestroy(const char* commandOptions[])
+{
+ ndn::nfd::FaceManagementOptions faceOptions;
+ const int faceId = boost::lexical_cast<int>(commandOptions[0]);
+ faceOptions.setFaceId(faceId);
+
+ startFaceCommand("destroy",
+ faceOptions,
+ bind(&Controller::onFaceSuccess, this, _1, "Face destroy succeeded"),
+ bind(&Controller::onError, this, _1, "Face destroy failed"));
+}
+
+void
+Controller::onFibSuccess(const ndn::nfd::FibManagementOptions& resp, const std::string& message)
+{
+ std::cout << resp << std::endl;
+}
+
+void
+Controller::onFaceSuccess(const ndn::nfd::FaceManagementOptions& resp, const std::string& message)
+{
+ std::cout << resp << std::endl;
+}
+
+void
+Controller::onError(const std::string& error, const std::string& message)
+{
+ throw Error(message + ": " + error);
+}
+}// namespace nfdc
+
+int
+main(int argc, char** argv)
+{
+ ndn::Face face;
+ nfdc::Controller p(face);
+
+ p.m_programName = argv[0];
+ int opt;
+ while ((opt = getopt(argc, argv, "h")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(p.m_programName);
+ return 0;
+
+ default:
+ usage(p.m_programName);
+ return 1;
+ }
+ }
+ try {
+ bool hasSucceeded = p.dispatch(argv[optind],
+ const_cast<const char**>(argv + optind + 1),
+ argc - optind - 1);
+ if (hasSucceeded == false) {
+ usage(p.m_programName);
+ return 1;
+ }
+
+ face.processEvents();
+ }
+ catch (const std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+ return 0;
+}
+
diff --git a/tools/nfdc.hpp b/tools/nfdc.hpp
new file mode 100644
index 0000000..a606100
--- /dev/null
+++ b/tools/nfdc.hpp
@@ -0,0 +1,135 @@
+/* -*- 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_TOOLS_NFDC_HPP
+#define NFD_TOOLS_NFDC_HPP
+
+#include <ndn-cpp-dev/face.hpp>
+#include <ndn-cpp-dev/management/controller.hpp>
+#include <ndn-cpp-dev/management/nfd-controller.hpp>
+#include <ndn-cpp-dev/management/nfd-fib-management-options.hpp>
+#include <ndn-cpp-dev/management/nfd-face-management-options.hpp>
+#include <vector>
+
+namespace nfdc {
+
+class Controller : public ndn::nfd::Controller
+{
+public:
+ struct Error : public std::runtime_error
+ {
+ Error(const std::string& what) : std::runtime_error(what) {}
+ };
+
+ explicit
+ Controller(ndn::Face& face);
+
+ ~Controller();
+
+ bool
+ dispatch(const std::string& cmd,
+ const char* cmdOptions[],
+ int nOptions);
+ /**
+ * \brief Create a new FIB entry if it doesn't exist
+ *
+ * cmd format:
+ * name
+ *
+ * @param cmdOptions add command without leading 'insert' component
+ */
+ void
+ fibInsert(const char* cmdOptions[]);
+ /**
+ * \brief Delete a FIB entry if it exists
+ *
+ * cmd format:
+ * name
+ *
+ * @param cmdOptions del command without leading 'delete' component
+ */
+ void
+ fibDelete(const char* cmdOptions[]);
+ /**
+ * \brief Adds a nexthop to an existing FIB entry
+ *
+ * If a nexthop of same FaceId exists on the FIB entry, its cost is updated.
+ * FaceId is the FaceId returned in NFD Face Management protocol.
+ * If FaceId is set to zero, it is implied as the face of the entity sending this command.
+ * cmd format:
+ * name faceId cost
+ *
+ * @param cmdOptions addNextHop command without leading 'add-nexthop' component
+ */
+ void
+ fibAddNextHop(const char* cmdOptions[], bool hasCost);
+ /**
+ * \brief Remove a nexthop from an existing FIB entry
+ *
+ * This command removes a nexthop from a FIB entry.
+ * Removing the last nexthop in a FIB entry will not automatically delete the FIB entry.
+ *
+ * cmd format:
+ * name faceId
+ *
+ * @param cmdOptions delNext command without leading 'remove-nexthop' component
+ */
+ void
+ fibRemoveNextHop(const char* cmdOptions[]);
+ /**
+ * \brief Sets a forwarding strategy for a namespace
+ *
+ * This command sets a forwarding strategy for a namespace.
+ *
+ * cmd format:
+ * name strategy
+ *
+ * @param cmdOptions setStrategy command without leading 'setStrategy' component
+ */
+ void
+ fibSetStrategy(const char* cmdOptions[]);
+
+ /**
+ * \brief create new face
+ *
+ * This command allows creation of UDP unicast and TCP faces only.
+ *
+ * cmd format:
+ * uri
+ *
+ * @param cmdOptions create command without leading 'create' component
+ */
+ void
+ faceCreate(const char* cmdOptions[]);
+ /**
+ * \brief destroy a face
+ *
+ * cmd format:
+ * faceId
+ *
+ * @param cmdOptions destroy command without leading 'destroy' component
+ */
+ void
+ faceDestroy(const char* cmdOptions[]);
+
+private:
+ void
+ onFibSuccess(const ndn::nfd::FibManagementOptions& fibOptions, const std::string& message);
+
+ void
+ onFaceSuccess(const ndn::nfd::FaceManagementOptions& faceOptions, const std::string& message);
+
+ void
+ onError(const std::string& error, const std::string& message);
+
+public:
+ const char* m_programName;
+};
+
+}// namespace nfdc
+
+#endif // NFD_TOOLS_NFDC_HPP
+
diff --git a/wscript b/wscript
index 3562e45..8591b35 100644
--- a/wscript
+++ b/wscript
@@ -2,6 +2,7 @@
VERSION='0.1'
from waflib import Build, Logs, Utils, Task, TaskGen, Configure
+import os
def options(opt):
opt.load('compiler_cxx')
@@ -83,8 +84,15 @@
source = 'daemon/main.cpp',
use = 'nfd-objects',
includes = [".", "daemon"],
- )
-
+ )
+
+ for app in bld.path.ant_glob('tools/*.cpp'):
+ bld(features=['cxx', 'cxxprogram'],
+ target = 'bin/%s' % (str(app.change_ext(''))),
+ source = ['tools/%s' % (str(app))],
+ use = 'BOOST NDN_CPP RT',
+ )
+
# Unit tests
if bld.env['WITH_TESTS']:
unit_tests = unittests = bld.program (
@@ -126,3 +134,4 @@
bld.fatal("ERROR: cannot build documentation (`doxygen' is not found in $PATH)")
bld(features="doxygen",
doxyfile='docs/doxygen.conf')
+