Initial commit
diff --git a/src/main/java/com/intel/jndn/management/NFD.java b/src/main/java/com/intel/jndn/management/NFD.java
new file mode 100644
index 0000000..e19f517
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/NFD.java
@@ -0,0 +1,303 @@
+/*
+ * File name: NFD.java
+ *
+ * Purpose: Helper class for interacting with an NDN forwarder daemon;
+ * see http://redmine.named-data.net/projects/nfd/wiki/Management for
+ * explanations of the various protocols used.
+ *
+ * © Copyright Intel Corporation. All rights reserved.
+ * Intel Corporation, 2200 Mission College Boulevard,
+ * Santa Clara, CA 95052-8119, USA
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.utils.Client;
+import java.io.IOException;
+import java.util.List;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.SecurityException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Helper class for interacting with an NDN forwarder daemon; see
+ * http://redmine.named-data.net/projects/nfd/wiki/Management for explanations
+ * of the various protocols used.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NFD {
+
+ public final static long DEFAULT_TIMEOUT = 2000;
+ private static final Logger logger = LogManager.getLogger();
+
+ /**
+ * Ping a forwarder on an existing face to verify that the forwarder is
+ * working and responding to requests; this version sends a discovery packet
+ * to /localhost/nfd which should always respond if the requestor is on the
+ * same machine as the NDN forwarding daemon.
+ *
+ * @param face
+ * @return true if successful, false otherwise
+ */
+ public static boolean pingLocal(Face face) {
+ return ping(face, new Name("/localhost/nfd"));
+ }
+
+ /**
+ * Request a name on an existing face to verify the forwarder is working and
+ * responding to requests. Note that the name must be served or cached on
+ * the forwarder for this to return true.
+ *
+ * @param face
+ * @param name
+ * @return true if successful, false otherwise
+ */
+ public static boolean ping(Face face, Name name) {
+ // build interest
+ Interest interest = new Interest(name);
+ interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+ interest.setMustBeFresh(true);
+
+ // send packet
+ Data data = Client.getDefault().getSync(face, interest);
+ return data != null;
+ }
+
+ /**
+ * Retrieve a list of faces and their status from the given forwarder; calls
+ * /localhost/nfd/faces/list which requires a local Face (all non-local
+ * packets are dropped)
+ *
+ * @param forwarder Only a localhost Face
+ * @return
+ * @throws Exception
+ */
+ public static List<FaceStatus> getFaceList(Face forwarder) throws Exception {
+ // build management Interest packet; see http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
+ Interest interest = new Interest(new Name("/localhost/nfd/faces/list"));
+ interest.setMustBeFresh(true);
+ interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
+ interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+
+ // TODO verify that all faces are being returned; right now they don't
+ // match up with the results from nfd-status-http-server but no
+ // additional segments are present;
+ // see http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
+ // send packet
+ Data data = Client.getDefault().getSync(forwarder, interest);
+ if (data == null) {
+ throw new Exception("Failed to retrieve list of faces from the forwarder.");
+ }
+
+ // parse packet
+ return FaceStatus.decode(data);
+ }
+
+ /**
+ * Helper method to register a new face on the forwarder; as mentioned at
+ * http://named-data.net/doc/NFD/current/manpages/nfdc.html, this is more
+ * for debugging; use 'register' instead
+ *
+ * @param forwarder Only a localhost Face
+ * @param faceId
+ * @param prefix
+ * @return
+ * @throws Exception
+ */
+ public static boolean addNextHop(Face forwarder, int faceId, Name prefix) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/fib/add-nexthop");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(prefix);
+ parameters.setFaceId(faceId);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ return sendCommandAndErrorCheck(forwarder, new Interest(command));
+ }
+
+ /**
+ * Create a new face on the given forwarder. Ensure the forwarding face is
+ * on the local machine (management requests are to /localhost/...) and that
+ * command signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder Only a localhost Face
+ * @param uri
+ * @return
+ * @throws java.lang.Exception
+ */
+ public static int createFace(Face forwarder, String uri) throws Exception {
+ Name command = new Name("/localhost/nfd/faces/create");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setUri(uri);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ ControlResponse response = sendCommand(forwarder, new Interest(command));
+
+ // check for body
+ if (response.Body.isEmpty()) {
+ throw new Exception("Failed to create face: " + uri);
+ }
+
+ // return
+ return response.Body.get(0).getFaceId();
+ }
+
+ /**
+ * Register a route on the forwarder; see
+ * http://named-data.net/doc/NFD/current/manpages/nfdc.html for command-line
+ * usage and http://redmine.named-data.net/projects/nfd/wiki/RibMgmt for
+ * protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder Only a localhost Face
+ * @param controlParameters
+ * @return
+ * @throws Exception
+ */
+ public static boolean register(Face forwarder, ControlParameters controlParameters) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/rib/register");
+ command.append(controlParameters.wireEncode());
+
+ // send the interest
+ return sendCommandAndErrorCheck(forwarder, new Interest(command));
+ }
+
+ /**
+ * Register a route on a forwarder; this will create a new face on the
+ * forwarder to the given URI/route pair. See register(Face,
+ * ControlParameters) for more details documentation.
+ *
+ * @param forwarder Only a localhost Face
+ * @param uri
+ * @param cost
+ * @param route
+ * @return true for successful registration
+ * @throws java.lang.Exception
+ */
+ public static boolean register(Face forwarder, String uri, Name route, int cost) throws Exception {
+ // create the new face
+ int faceId = createFace(forwarder, uri);
+
+ // run base method
+ return register(forwarder, faceId, route, cost);
+ }
+
+ /**
+ * Register a route on a forwarder; this will not create a new face since it
+ * is provided a faceId. See register(Face, ControlParameters) for full
+ * documentation
+ *
+ * @param forwarder Only a localhost Face
+ * @param faceId
+ * @param route
+ * @param cost
+ * @return true for successful registration
+ * @throws java.lang.Exception
+ */
+ public static boolean register(Face forwarder, int faceId, Name route, int cost) throws Exception {
+ // build command name
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(route);
+ parameters.setFaceId(faceId);
+ parameters.setCost(cost);
+ ForwardingFlags flags = new ForwardingFlags();
+ flags.setCapture(true);
+ flags.setChildInherit(true);
+ parameters.setForwardingFlags(flags);
+
+ // run base method
+ return register(forwarder, parameters);
+ }
+
+ /**
+ * Set a strategy on the forwarder; see
+ * http://named-data.net/doc/NFD/current/manpages/nfdc.html for command-line
+ * usage and http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder Only a localhost Face
+ * @param prefix
+ * @param strategy
+ * @return true for successful command
+ * @throws Exception
+ */
+ public static boolean setStrategy(Face forwarder, Name prefix, Name strategy) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/strategy-choice/set");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(prefix);
+ parameters.setStrategy(strategy);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ return sendCommandAndErrorCheck(forwarder, new Interest(command));
+ }
+
+ /**
+ * Send an interest as a command to the forwarder; this method will convert
+ * the interest to a command interest and block until a response is received
+ * from the forwarder. Ensure the forwarding face is on the local machine
+ * (management requests are to /localhost/...) and that command signing has
+ * been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder Only a localhost Face
+ * @param interest As described at
+ * http://redmine.named-data.net/projects/nfd/wiki/ControlCommand, the
+ * requested interest must have encoded ControlParameters appended to the
+ * interest name
+ * @return
+ * @throws net.named_data.jndn.security.SecurityException
+ * @throws java.io.IOException
+ * @throws net.named_data.jndn.encoding.EncodingException
+ */
+ public static ControlResponse sendCommand(Face forwarder, Interest interest) throws SecurityException, IOException, EncodingException {
+ forwarder.makeCommandInterest(interest);
+
+ // send command packet
+ Data data = Client.getDefault().getSync(forwarder, interest);
+ if (data == null) {
+ throw new IOException("Failed to receive command response.");
+ }
+
+ // return response
+ return ControlResponse.decode(data);
+ }
+
+ /**
+ * Send an interest as a command to the forwarder; this method will convert
+ * the interest to a command interest and block until a response is received
+ * from the forwarder.
+ *
+ * @param forwarder Only a localhost Face
+ * @param interest As described at
+ * http://redmine.named-data.net/projects/nfd/wiki/ControlCommand, the
+ * requested interest must have encoded ControlParameters appended to the
+ * interest name
+ * @return
+ * @throws net.named_data.jndn.security.SecurityException
+ * @throws java.io.IOException
+ * @throws net.named_data.jndn.encoding.EncodingException
+ */
+ public static boolean sendCommandAndErrorCheck(Face forwarder, Interest interest) throws SecurityException, IOException, EncodingException {
+ ControlResponse response = sendCommand(forwarder, interest);
+ if (response.StatusCode < 400) {
+ return true;
+ } else {
+ logger.warn("Command sent but failed: " + response.StatusCode + " " + response.StatusText);
+ return false;
+ }
+ }
+}