Major refactoring and improvements

This commit removes dependency on jndn-utils (it requires Java 8, while
jndn-management needs to be compatible with Java 7 to support Android)

Some changes include interface unification with structures in ndn-cxx library

Change-Id: I944ea41e225edc1848657ed574b625c7ec18df5d
diff --git a/README-dev.md b/README-dev.md
index b2aecd3..1219d30 100644
--- a/README-dev.md
+++ b/README-dev.md
@@ -43,7 +43,7 @@
 To run a specific test or test case, use `--tests=<test-name>[.<test-case>]` command-line option. For example,
 
     ./gradlew test --tests *FaceStatusTest
-    ./gradlew test --tests *FaceStatusTest.Encode
+    ./gradlew test --tests *FaceStatusTest.testEncode
 
 ### Integration Tests
 
diff --git a/build.gradle b/build.gradle
index 1f498c8..aed58db 100644
--- a/build.gradle
+++ b/build.gradle
@@ -55,7 +55,6 @@
 
 dependencies {
   compile 'net.named-data:jndn:0.10'
-  compile 'com.intel.jndn.utils:jndn-utils:1.0.0'
 
   testCompile 'junit:junit:4.12'
   testCompile 'com.intel.jndn.mock:jndn-mock:1.0.1'
diff --git a/pom.xml b/pom.xml
index 4fb35f8..731071f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,11 +40,6 @@
       <artifactId>jndn</artifactId>
       <version>0.10</version>
     </dependency>
-    <dependency>
-      <groupId>com.intel.jndn.utils</groupId>
-      <artifactId>jndn-utils</artifactId>
-      <version>1.0.1-SNAPSHOT</version>
-    </dependency>
     <!-- test dependencies -->
     <dependency>
       <groupId>junit</groupId>
diff --git a/src/main/java/com/intel/jndn/management/ManagementException.java b/src/main/java/com/intel/jndn/management/ManagementException.java
index 70a15d1..69e209a 100644
--- a/src/main/java/com/intel/jndn/management/ManagementException.java
+++ b/src/main/java/com/intel/jndn/management/ManagementException.java
@@ -11,11 +11,10 @@
  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  * more details.
  */
+
 package com.intel.jndn.management;
 
 import com.intel.jndn.management.types.ControlResponse;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
 
 /**
  * Represent a failure to correctly manage the NDN Forwarding Daemon (NFD).
@@ -25,19 +24,15 @@
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class ManagementException extends Exception {
-
   /**
-   *
-   * @param message
+   * Constructor from the message
    */
   public ManagementException(String message) {
     super(message);
   }
 
   /**
-   *
-   * @param message
-   * @param cause
+   * Constructor from the message and the cause
    */
   public ManagementException(String message, Throwable cause) {
     super(message, cause);
@@ -45,26 +40,6 @@
 
   /**
    * Parse an NFD response to create a ManagementException.
-   *
-   * @param forwarderResponse
-   * @return
-   */
-  public static ManagementException fromResponse(Blob forwarderResponse) {
-    ControlResponse response = new ControlResponse();
-    try {
-      response.wireDecode(forwarderResponse.buf());
-      String message = "Action failed, forwarder returned: " + response.getStatusCode() + " " + response.getStatusText();
-      return new ManagementException(message);
-    } catch (EncodingException e) {
-      return new ManagementException("Action failed and forwarder response was unparseable.", e);
-    }
-  }
-
-  /**
-   * Parse an NFD response to create a ManagementException.
-   *
-   * @param forwarderResponse
-   * @return
    */
   public static ManagementException fromResponse(ControlResponse response) {
     String message = "Action failed, forwarder returned: " + response.getStatusCode() + " " + response.getStatusText();
diff --git a/src/main/java/com/intel/jndn/management/NFD.java b/src/main/java/com/intel/jndn/management/NFD.java
deleted file mode 100644
index 9eb9180..0000000
--- a/src/main/java/com/intel/jndn/management/NFD.java
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.StatusDataset;
-import com.intel.jndn.management.types.ControlResponse;
-import com.intel.jndn.management.types.FaceStatus;
-import com.intel.jndn.management.types.FibEntry;
-import com.intel.jndn.management.types.ForwarderStatus;
-import com.intel.jndn.management.types.LocalControlHeader;
-import com.intel.jndn.management.types.RibEntry;
-import com.intel.jndn.management.types.StrategyChoice;
-import com.intel.jndn.utils.client.impl.AdvancedClient;
-import com.intel.jndn.utils.client.impl.SimpleClient;
-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 java.util.logging.Logger;
-import net.named_data.jndn.KeyLocator;
-
-/**
- * Helper class for interacting with an NDN forwarder daemon; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/Management">http://redmine.named-data.net/projects/nfd/wiki/Management</a>
- * for explanations of the various protocols used.
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class NFD {
-
-  public final static long DEFAULT_TIMEOUT = 2000;
-  public final static int OK_STATUS = 200;
-  static private final Logger logger = Logger.getLogger(NFD.class.getName());
-
-  /**
-   * 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 only a localhost 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 a {@link Face} to ping
-   * @param name a known {@link Name} that the remote node will answer to
-   * @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
-    try {
-      Data data = SimpleClient.getDefault().getSync(face, interest);
-      return data != null;
-    } catch (IOException e) {
-      return false;
-    }
-  }
-
-  /**
-   * Retrieve the status of the given forwarder; calls /localhost/nfd/status
-   * which requires a local Face (all non-local packets are dropped)
-   *
-   * @param forwarder only a localhost Face
-   * @return the forwarder status object, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus">
-   * http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus</a>.
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the returned status could not be decoded
-   */
-  public static ForwarderStatus getForwarderStatus(Face forwarder) throws IOException, EncodingException {
-    Data data = retrieveStatus(forwarder);
-    ForwarderStatus status = new ForwarderStatus();
-    status.wireDecode(data.getContent().buf());
-    return status;
-  }
-
-  /**
-   * 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 a list of face status objects, see
-   * http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt.
-   * @throws IOException if the network request failed
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static List<FaceStatus> getFaceList(Face forwarder) throws IOException, ManagementException {
-    Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/faces/list"));
-    return StatusDataset.wireDecode(data.getContent(), FaceStatus.class);
-  }
-
-  /**
-   * Retrieve a list of FIB entries and their NextHopRecords from the given
-   * forwarder; calls /localhost/nfd/fib/list which requires a local Face (all
-   * non-local packets are dropped).
-   *
-   * @param forwarder only a localhost Face
-   * @return a list of FIB entries, see
-   * http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset.
-   * @throws IOException if the network request failed
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static List<FibEntry> getFibList(Face forwarder) throws IOException, ManagementException {
-    Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/fib/list"));
-    return StatusDataset.wireDecode(data.getContent(), FibEntry.class);
-  }
-
-  /**
-   * Retrieve a list of routing entries from the RIB; calls
-   * /localhost/nfd/rib/list which requires a local Face (all non-local packets
-   * are dropped).
-   *
-   * @param forwarder only a localhost Face
-   * @return a list of RIB entries, i.e. routes, see
-   * http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset.
-   * @throws IOException if the network request failed
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static List<RibEntry> getRouteList(Face forwarder) throws IOException, ManagementException {
-    Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/rib/list"));
-    return StatusDataset.wireDecode(data.getContent(), RibEntry.class);
-  }
-
-  /**
-   * Retrieve the list of strategy choice entries from the NFD; calls
-   * /localhost/nfd/rib/list which requires a local Face (all non-local packets
-   * are dropped).
-   *
-   * @param forwarder only a localhost Face
-   * @return a list of strategy choice entries, i.e. routes, see
-   * http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice.
-   * @throws IOException if the network request failed
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static List<StrategyChoice> getStrategyList(Face forwarder) throws IOException, ManagementException {
-    Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/strategy-choice/list"));
-    return StatusDataset.wireDecode(data.getContent(), StrategyChoice.class);
-  }
-
-  /**
-   * Retrieve the {@link KeyLocator} for an NFD.
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @return the {@link KeyLocator} of the NFD's key
-   * @throws IOException if the network request failed
-   * @throws ManagementException if the NFD rejected the request or no
-   * KeyLocator was found
-   */
-  public static KeyLocator getKeyLocator(Face forwarder) throws ManagementException, IOException {
-    Data data = retrieveStatus(forwarder);
-    if (!KeyLocator.canGetFromSignature(data.getSignature())) {
-      throw new ManagementException("No key locator available.");
-    }
-    return KeyLocator.getFromSignature(data.getSignature());
-  }
-
-  /**
-   * Helper method to register a new face on the forwarder; as mentioned at
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>,
-   * this is more for debugging; use 'register' instead
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @param faceId the ID of the face to add, see
-   * {@link #createFace(net.named_data.jndn.Face, java.lang.String)} for
-   * creating this
-   * @param prefix the {@link Name} of the next-hop prefix
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void addNextHop(Face forwarder, int faceId, Name prefix) throws IOException, EncodingException, ManagementException {
-    // 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
-    sendCommand(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 {@link Face}
-   * @param uri a string like "tcp4://host.name.com" (see nfd-status channels
-   * for more protocol options)
-   * @return the newly created face ID
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static int createFace(Face forwarder, String uri) throws IOException, EncodingException, ManagementException {
-    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));
-
-    // return
-    return response.getBody().get(0).getFaceId();
-  }
-
-  /**
-   * Destroy a face on 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 {@link Face}
-   * @param faceId the ID of the face to destroy
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void destroyFace(Face forwarder, int faceId) throws IOException, EncodingException, ManagementException {
-    Name command = new Name("/localhost/nfd/faces/destroy");
-    ControlParameters parameters = new ControlParameters();
-    parameters.setFaceId(faceId);
-    command.append(parameters.wireEncode());
-
-    // send the interest
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Enable a local control feature on the given forwarder. See
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature</a>
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @param header the control feature to enable
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void enableLocalControlHeader(Face forwarder, LocalControlHeader header) throws IOException, EncodingException, ManagementException {
-    // build command name
-    Name command = new Name("/localhost/nfd/faces/enable-local-control");
-    ControlParameters parameters = new ControlParameters();
-    parameters.setLocalControlFeature(header.getNumericValue());
-    command.append(parameters.wireEncode());
-
-    // send command and return
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Disable a local control feature on the given forwarder. See
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature</a>
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @param header the control feature to disable
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void disableLocalControlHeader(Face forwarder, LocalControlHeader header) throws IOException, EncodingException, ManagementException {
-    // build command name
-    Name command = new Name("/localhost/nfd/faces/disable-local-control");
-    ControlParameters parameters = new ControlParameters();
-    parameters.setLocalControlFeature(header.getNumericValue());
-    command.append(parameters.wireEncode());
-
-    // send command and return
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Register a route on the forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
-   * 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 {@link Face}
-   * @param controlParameters the {@link ControlParameters} command options
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void register(Face forwarder, ControlParameters controlParameters) throws IOException, EncodingException, ManagementException {
-    // build command name
-    Name command = new Name("/localhost/nfd/rib/register");
-    command.append(controlParameters.wireEncode());
-
-    // send the interest
-    sendCommand(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 detailed documentation.
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @param uri the URI (e.g. "tcp4://10.10.2.2:6363") of the remote node; note
-   * that this must be one of the canonical forms described in the wiki
-   * (http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#TCP) for NFD to
-   * accept the registration--otherwise you will see 400 errors
-   * @param route the {@link Name} prefix of the route
-   * @param cost the numeric cost of forwarding along the route
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void register(Face forwarder, String uri, Name route, int cost) throws IOException, EncodingException, ManagementException {
-    // create the new face
-    int faceId = createFace(forwarder, uri);
-
-    // run base method
-    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 {@link Face}
-   * @param faceId the ID of the {@link Face} to assign to the route
-   * @param route the {@link Name} prefix of the route
-   * @param cost the numeric cost of forwarding along the route
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void register(Face forwarder, int faceId, Name route, int cost) throws IOException, EncodingException, ManagementException {
-    // 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
-    register(forwarder, parameters);
-  }
-
-  /**
-   * Unregister a route on a forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
-   * 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 {@link Face}
-   * @param controlParameters the {@link ControlParameters} command options
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void unregister(Face forwarder, ControlParameters controlParameters) throws IOException, EncodingException, ManagementException {
-    // build command name
-    Name command = new Name("/localhost/nfd/rib/unregister");
-    command.append(controlParameters.wireEncode());
-
-    // send the interest
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Unregister a route on a forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
-   * 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 {@link Face}
-   * @param route the {@link Name} prefix of the route
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void unregister(Face forwarder, Name route) throws IOException, EncodingException, ManagementException {
-    // build command name
-    ControlParameters controlParameters = new ControlParameters();
-    controlParameters.setName(route);
-
-    // send the interest
-    unregister(forwarder, controlParameters);
-  }
-
-  /**
-   * Unregister a route on a forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
-   * 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 {@link Face}
-   * @param route the {@link Name} prefix of the route
-   * @param faceId the specific ID of the face to remove (more than one face can
-   * be registered to a route)
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void unregister(Face forwarder, Name route, int faceId) throws IOException, EncodingException, ManagementException {
-    // build command name
-    ControlParameters controlParameters = new ControlParameters();
-    controlParameters.setName(route);
-    controlParameters.setFaceId(faceId);
-
-    // send the interest
-    unregister(forwarder, controlParameters);
-  }
-
-  /**
-   * Unregister a route on a forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
-   * 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 {@link Face}
-   * @param route the {@link Name} prefix of the route
-   * @param uri the URI (e.g. "tcp4://some.host.com") of the remote node (more
-   * than one face can be registered to a route)
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void unregister(Face forwarder, Name route, String uri) throws IOException, EncodingException, ManagementException {
-    int faceId = -1;
-    for (FaceStatus face : getFaceList(forwarder)) {
-      if (face.getUri().matches(uri)) {
-        faceId = face.getFaceId();
-        break;
-      }
-    }
-
-    if (faceId == -1) {
-      throw new ManagementException("Face not found: " + uri);
-    }
-
-    // send the interest
-    unregister(forwarder, route, faceId);
-  }
-
-  /**
-   * Set a strategy on the forwarder; see
-   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
-   * for command-line usage and
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice</a>
-   * 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 {@link Face}
-   * @param prefix the {@link Name} prefix
-   * @param strategy the {@link Name} of the strategy to set, e.g.
-   * /localhost/nfd/strategy/broadcast
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void setStrategy(Face forwarder, Name prefix, Name strategy) throws IOException, EncodingException, ManagementException {
-    // 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
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Set a strategy on the forwarder; see
-   * {@link #setStrategy(net.named_data.jndn.Face, net.named_data.jndn.Name, net.named_data.jndn.Name)}
-   * for more information. 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 {@link Face}
-   * @param prefix the {@link Name} prefix
-   * @throws IOException if the network request failed
-   * @throws EncodingException if the NFD response could not be decoded
-   * @throws ManagementException if the NFD rejected the request
-   */
-  public static void unsetStrategy(Face forwarder, Name prefix) throws IOException, EncodingException, ManagementException {
-    // build command name
-    Name command = new Name("/localhost/nfd/strategy-choice/unset");
-    ControlParameters parameters = new ControlParameters();
-    parameters.setName(prefix);
-    command.append(parameters.wireEncode());
-
-    // send the interest
-    sendCommand(forwarder, new Interest(command));
-  }
-
-  /**
-   * Build an interest to retrieve the NFD status.
-   *
-   * @param forwarder only a localhost {@link Face}
-   * @return the status {@link Data} packet
-   * @throws IOException if the retrieval fails
-   */
-  private static Data retrieveStatus(Face forwarder) throws IOException {
-    Interest interest = new Interest(new Name("/localhost/nfd/status"));
-    interest.setMustBeFresh(true);
-    interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
-    interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
-    Data data = SimpleClient.getDefault().getSync(forwarder, interest);
-    return data;
-  }
-
-  /**
-   * Build an interest to retrieve a segmented data set from the NFD; for
-   * details on the DataSet, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-   *
-   * @param forwarder the {@link Face} to an NFD
-   * @param datasetName the {@link Name} of the dataset to retrieve
-   * @return the re-assembled {@link Data} packet
-   * @throws IOException if the request fails
-   * @throws ManagementException if the returned TLV is not the expected type
-   */
-  public static Data retrieveDataSet(Face forwarder, Name datasetName) throws IOException, ManagementException {
-    // build management Interest packet; see <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-    Interest interest = new Interest(datasetName);
-    interest.setMustBeFresh(true);
-    interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
-    interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
-
-    // send packet
-    Data data = AdvancedClient.getDefault().getSync(forwarder, interest);
-
-    // check for failed request
-    if (data.getContent().buf().get(0) == ControlResponse.TLV_CONTROL_RESPONSE) {
-      throw ManagementException.fromResponse(data.getContent());
-    }
-
-    return 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. 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, command signing info must be set
-   * @param interest As described at
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,</a>
-   * the requested interest must have encoded ControlParameters appended to the
-   * interest name
-   * @return a {@link ControlResponse}
-   * @throws java.io.IOException
-   * @throws net.named_data.jndn.encoding.EncodingException
-   * @throws com.intel.jndn.management.ManagementException
-   */
-  public static ControlResponse sendCommand(Face forwarder, Interest interest) throws IOException, EncodingException, ManagementException {
-    // forwarder must have command signing info set
-    try {
-      forwarder.makeCommandInterest(interest);
-    } catch (SecurityException e) {
-      throw new IllegalArgumentException("Failed to make command interest; ensure command signing info is set on the face.", e);
-    }
-
-    // send command packet
-    Data data = SimpleClient.getDefault().getSync(forwarder, interest);
-
-    // decode response
-    ControlResponse response = new ControlResponse();
-    response.wireDecode(data.getContent().buf());
-
-    // check response for success
-    if (response.getStatusCode() != OK_STATUS) {
-      throw ManagementException.fromResponse(response);
-    }
-
-    return response;
-  }
-}
diff --git a/src/main/java/com/intel/jndn/management/NdnPingClient.java b/src/main/java/com/intel/jndn/management/NdnPingClient.java
new file mode 100644
index 0000000..ecdaecc
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/NdnPingClient.java
@@ -0,0 +1,73 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.management.helpers.FetchHelper;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class NdnPingClient {
+  public static final long DEFAULT_TIMEOUT = 2000;
+  private static final Logger LOG = Logger.getLogger(NdnPingClient.class.getName());
+
+  /**
+   * Prevent creation of NdnPingClient instances
+   */
+  private NdnPingClient() {
+  }
+
+  /**
+   * 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 only a localhost 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 a {@link Face} to ping
+   * @param name a known {@link Name} that the remote node will answer to
+   * @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
+    try {
+      Data data = FetchHelper.getData(face, interest);
+      return data != null;
+    } catch (IOException e) {
+      LOG.log(Level.INFO, "Error sending ping interest", e);
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/Nfdc.java b/src/main/java/com/intel/jndn/management/Nfdc.java
new file mode 100644
index 0000000..ae94417
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/Nfdc.java
@@ -0,0 +1,573 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.management.enums.RouteOrigin;
+import com.intel.jndn.management.helpers.FetchHelper;
+import com.intel.jndn.management.helpers.StatusDatasetHelper;
+import net.named_data.jndn.*;
+import com.intel.jndn.management.types.ControlResponse;
+import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.FibEntry;
+import com.intel.jndn.management.types.ForwarderStatus;
+import com.intel.jndn.management.enums.LocalControlHeader;
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.StrategyChoice;
+import java.io.IOException;
+import java.util.List;
+
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.SecurityException;
+
+/**
+ * Helper class for interacting with an NDN forwarder daemon; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/Management">NFD Management</a>
+ * for explanations of the various protocols used.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class Nfdc {
+  private static final int OK_STATUS = 200;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Prevent creation of Nfdc instances
+   */
+  private Nfdc() {
+  }
+
+  /**
+   * Retrieve the status of the given forwarder; calls /localhost/nfd/status/general
+   * which requires a local Face (all non-local packets are dropped)
+   *
+   * @param face only a localhost Face
+   * @return the forwarder status object
+   *
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus">ForwarderStatus</a>
+   * @throws ManagementException if the network request failed or the returned status could not be decoded
+   */
+  public static ForwarderStatus getForwarderStatus(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/status/general"));
+      return new ForwarderStatus(StatusDatasetHelper.combine(segments));
+    } catch (IOException|EncodingException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * 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 face only a localhost Face
+   * @return a list of face status objects
+   *
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt">FaceManagement</a>
+   * @throws ManagementException if the network request failed or if the NFD rejected the request
+   */
+  public static List<FaceStatus> getFaceList(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/faces/list"));
+      return StatusDatasetHelper.wireDecode(segments, FaceStatus.class);
+    } catch (IOException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Retrieve a list of FIB entries and their NextHopRecords from the given
+   * forwarder; calls /localhost/nfd/fib/list which requires a local Face (all
+   * non-local packets are dropped).
+   *
+   * @param face only a localhost Face
+   * @return a list of FIB entries
+   *
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">FIB Dataset</a>
+   * @throws ManagementException if the network request failed or if the NFD rejected the request
+   */
+  public static List<FibEntry> getFibList(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/fib/list"));
+      return StatusDatasetHelper.wireDecode(segments, FibEntry.class);
+    } catch (IOException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Retrieve a list of routing entries from the RIB; calls
+   * /localhost/nfd/rib/list which requires a local Face (all non-local packets
+   * are dropped).
+   *
+   * @param face only a localhost Face
+   * @return a list of RIB entries, i.e. routes
+   *
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">RIB Dataset</a>
+   * @throws ManagementException if the network request failed or if the NFD rejected the request
+   */
+  public static List<RibEntry> getRouteList(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/rib/list"));
+      return StatusDatasetHelper.wireDecode(segments, RibEntry.class);
+    } catch (IOException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Retrieve the list of strategy choice entries from the NFD; calls
+   * /localhost/nfd/rib/list which requires a local Face (all non-local packets
+   * are dropped).
+   *
+   * @param face only a localhost Face
+   * @return a list of strategy choice entries, i.e. routes
+   *
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">StrategyChoice</a>
+   * @throws ManagementException  if the network request failed, the NFD response could not be decoded, or
+   *                              the NFD rejected the request
+   */
+  public static List<StrategyChoice> getStrategyList(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/strategy-choice/list"));
+      return StatusDatasetHelper.wireDecode(segments, StrategyChoice.class);
+    } catch (IOException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Retrieve the {@link KeyLocator} for an NFD.
+   *
+   * @param face only a localhost {@link Face}
+   * @return the {@link KeyLocator} of the NFD's key
+   * @throws ManagementException if the network request failed, if the NFD rejected the request, or no
+   *                             KeyLocator was found
+   */
+  public static KeyLocator getKeyLocator(Face face) throws ManagementException {
+    try {
+      List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/status/general"));
+      if (segments.isEmpty() || !KeyLocator.canGetFromSignature(segments.get(0).getSignature())) {
+        throw new ManagementException("No key locator available.");
+      }
+      return KeyLocator.getFromSignature(segments.get(0).getSignature());
+    } catch (IOException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * 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 face only a localhost {@link Face}
+   * @param uri       a string like "tcp4://host.name.com" (see nfd-status channels
+   *                  for more protocol options)
+   * @return the newly created face ID
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static int createFace(Face face, String uri) throws ManagementException {
+    Name command = new Name("/localhost/nfd/faces/create");
+    ControlParameters parameters = new ControlParameters();
+    parameters.setUri(uri);
+    command.append(parameters.wireEncode());
+
+    try {
+      // send the interest
+      ControlResponse response = sendCommand(face, command);
+
+      // return
+      return response.getBody().get(0).getFaceId();
+    }
+    catch (IOException|EncodingException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Destroy a face on 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 face only a localhost {@link Face}
+   * @param faceId    the ID of the face to destroy
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void destroyFace(Face face, int faceId) throws ManagementException {
+    Name command = new Name("/localhost/nfd/faces/destroy");
+    ControlParameters parameters = new ControlParameters();
+    parameters.setFaceId(faceId);
+    command.append(parameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Enable a local control feature on the given forwarder. See
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature</a>
+   *
+   * @param face only a localhost {@link Face}
+   * @param header    the control feature to enable
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void enableLocalControlHeader(Face face, LocalControlHeader header) throws ManagementException {
+    // build command name
+    Name command = new Name("/localhost/nfd/faces/enable-local-control");
+    ControlParameters parameters = new ControlParameters();
+    parameters.setLocalControlFeature(header.toInteger());
+    command.append(parameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Disable a local control feature on the given forwarder. See
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature</a>
+   *
+   * @param face only a localhost {@link Face}
+   * @param header    the control feature to disable
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void disableLocalControlHeader(Face face, LocalControlHeader header) throws ManagementException {
+    // build command name
+    Name command = new Name("/localhost/nfd/faces/disable-local-control");
+    ControlParameters parameters = new ControlParameters();
+    parameters.setLocalControlFeature(header.toInteger());
+    command.append(parameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Register a route on the forwarder; see
+   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+   * for command-line usage and
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+   * 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 face         only a localhost {@link Face}
+   * @param controlParameters the {@link ControlParameters} command options
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void register(Face face, ControlParameters controlParameters) throws ManagementException {
+    // build command name
+    Name command = new Name("/localhost/nfd/rib/register");
+    command.append(controlParameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Register a route on a forwarder; this will create a new face on the
+   * forwarder towards the face (e.g., self registration)
+   *
+   * @param face only a localhost {@link Face}
+   * @param route     the {@link Name} prefix of the route
+   * @param cost      the numeric cost of forwarding along the route
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void register(Face face, Name route, int cost) throws ManagementException {
+    ForwardingFlags flags = new ForwardingFlags();
+    flags.setCapture(false);
+    flags.setChildInherit(true);
+
+    register(face, new ControlParameters()
+      .setName(route)
+      .setCost(cost)
+      .setOrigin(RouteOrigin.APP.toInteger())
+      .setForwardingFlags(flags));
+  }
+  /**
+   * 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 detailed documentation.
+   *
+   * @param face only a localhost {@link Face}
+   * @param uri       the URI (e.g. "tcp4://10.10.2.2:6363") of the remote node; note
+   *                  that this must be one of the canonical forms described in the wiki
+   *                  (http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#TCP) for NFD to
+   *                  accept the registration--otherwise you will see 400 errors
+   * @param route     the {@link Name} prefix of the route
+   * @param cost      the numeric cost of forwarding along the route
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void register(Face face, String uri, Name route, int cost) throws ManagementException {
+    // create the new face
+    int faceId = createFace(face, uri);
+
+    // run base method
+    register(face, 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 {@link Face}
+   * @param faceId    the ID of the {@link Face} to assign to the route
+   * @param route     the {@link Name} prefix of the route
+   * @param cost      the numeric cost of forwarding along the route
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void register(Face forwarder, int faceId, Name route, int cost) throws ManagementException {
+    // build command name
+    ControlParameters parameters = new ControlParameters();
+    parameters.setName(route);
+    parameters.setFaceId(faceId);
+    parameters.setCost(cost);
+    parameters.setOrigin(RouteOrigin.STATIC.toInteger());
+    ForwardingFlags flags = new ForwardingFlags();
+    flags.setCapture(false);
+    flags.setChildInherit(true);
+    parameters.setForwardingFlags(flags);
+
+    // run base method
+    register(forwarder, parameters);
+  }
+
+  /**
+   * Unregister a route on a forwarder; see
+   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+   * for command-line usage and
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+   * 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 face         only a localhost {@link Face}
+   * @param controlParameters the {@link ControlParameters} command options
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void unregister(Face face, ControlParameters controlParameters) throws ManagementException {
+    // build command name
+    Name command = new Name("/localhost/nfd/rib/unregister");
+    command.append(controlParameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Unregister a route on a forwarder; see
+   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+   * for command-line usage and
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+   * 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 face only a localhost {@link Face}
+   * @param route     the {@link Name} prefix of the route
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void unregister(Face face, Name route) throws ManagementException {
+    // build command name
+    ControlParameters controlParameters = new ControlParameters();
+    controlParameters.setName(route);
+
+    // send the interest
+    unregister(face, controlParameters);
+  }
+
+  /**
+   * Unregister a route on a forwarder; see
+   * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+   * for command-line usage and
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+   * 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 face only a localhost {@link Face}
+   * @param route     the {@link Name} prefix of the route
+   * @param faceId    the specific ID of the face to remove (more than one face can
+   *                  be registered to a route)
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void unregister(Face face, Name route, int faceId) throws ManagementException {
+    // build command name
+    ControlParameters controlParameters = new ControlParameters();
+    controlParameters.setName(route);
+    controlParameters.setFaceId(faceId);
+
+    // send the interest
+    unregister(face, controlParameters);
+  }
+
+  /**
+   * Unregister a route on a forwarder
+   *
+   * Ensure the forwarding face is on the local machine (management requests are to /localhost/...) and that command
+   * signing has been set up using forwarder.setCommandSigningInfo().
+   *
+   * @param face only a localhost {@link Face}
+   * @param route     the {@link Name} prefix of the route
+   * @param uri       the URI (e.g. "tcp4://some.host.com") of the remote node (more
+   *                  than one face can be registered to a route)
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   * @see <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">nfdc command-line usage</a>
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">RibMgmt</a>
+   */
+  public static void unregister(Face face, Name route, String uri) throws ManagementException {
+    int faceId = -1;
+    for (FaceStatus faceStatus : getFaceList(face)) {
+      if (faceStatus.getRemoteUri().matches(uri)) {
+        faceId = faceStatus.getFaceId();
+        break;
+      }
+    }
+
+    if (faceId == -1) {
+      throw new ManagementException("Face not found: " + uri);
+    }
+
+    // send the interest
+    unregister(face, route, faceId);
+  }
+
+  /**
+   * Set a strategy on the forwarder
+   *
+   * Ensure the forwarding face is on the local machine (management requests are to /localhost/...) and that command
+   * signing has been set up using forwarder.setCommandSigningInfo().
+   *
+   * @param face only a localhost {@link Face}
+   * @param prefix    the {@link Name} prefix
+   * @param strategy  the {@link Name} of the strategy to set, e.g.
+   *                  /localhost/nfd/strategy/broadcast
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   * @see <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">nfdc command-line usage</a>
+   * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">StrategyChoice</a>
+   */
+  public static void setStrategy(Face face, Name prefix, Name strategy) throws ManagementException {
+    // 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());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * Set a strategy on the forwarder; see
+   * {@link #setStrategy(net.named_data.jndn.Face, net.named_data.jndn.Name, net.named_data.jndn.Name)}
+   * for more information. 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 face only a localhost {@link Face}
+   * @param prefix    the {@link Name} prefix
+   * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
+   *                             the NFD rejected the request
+   */
+  public static void unsetStrategy(Face face, Name prefix) throws ManagementException {
+    // build command name
+    Name command = new Name("/localhost/nfd/strategy-choice/unset");
+    ControlParameters parameters = new ControlParameters();
+    parameters.setName(prefix);
+    command.append(parameters.wireEncode());
+
+    try {
+      sendCommand(face, command);
+    } catch (IOException|EncodingException|ManagementException e) {
+      throw new ManagementException(e.getMessage(), e);
+    }
+  }
+
+  /**
+   * 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 face only a localhost Face, command signing info must be set
+   * @param name As described at
+   *             <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,</a>
+   *             the requested interest must have encoded ControlParameters appended to the
+   *             interest name
+   * @return a {@link ControlResponse}
+   * @throws java.io.IOException
+   * @throws net.named_data.jndn.encoding.EncodingException
+   * @throws ManagementException
+   */
+  private static ControlResponse
+  sendCommand(Face face, Name name) throws IOException, EncodingException, ManagementException {
+    Interest interest = new Interest(name);
+
+    // forwarder must have command signing info set
+    try {
+      face.makeCommandInterest(interest);
+    } catch (SecurityException e) {
+      throw new IllegalArgumentException("Failed to make command interest; ensure command signing info is set on the face.", e);
+    }
+
+    // send command packet
+    Data data = FetchHelper.getData(face, interest.getName());
+
+    // decode response
+    ControlResponse response = new ControlResponse();
+    response.wireDecode(data.getContent().buf());
+
+    // check response for success
+    if (response.getStatusCode() != OK_STATUS) {
+      throw ManagementException.fromResponse(response);
+    }
+
+    return response;
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/enums/FacePersistency.java b/src/main/java/com/intel/jndn/management/enums/FacePersistency.java
new file mode 100644
index 0000000..08630b8
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/FacePersistency.java
@@ -0,0 +1,54 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.enums;
+
+/**
+ * Indicate whether the face is persistent; used by FaceStatus
+ *
+ * @see <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">FaceMgmt</a>
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public enum FacePersistency {
+  NONE(-1), // invalid value
+
+  PERSISTENT(0),
+  ON_DEMAND(1),
+  PERMANENT(2);
+
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  FacePersistency(int value) {
+    this.value = value;
+  }
+
+  public final int toInteger() {
+    return value;
+  }
+
+  public static FacePersistency
+  fromInteger(int value) {
+    switch (value) {
+      case 0:
+        return PERSISTENT;
+      case 1:
+        return ON_DEMAND;
+      case 2:
+        return PERMANENT;
+      default:
+        return NONE;
+    }
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/enums/FaceScope.java b/src/main/java/com/intel/jndn/management/enums/FaceScope.java
new file mode 100644
index 0000000..d32621e
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/FaceScope.java
@@ -0,0 +1,53 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.enums;
+
+/**
+ * Indicate whether the face is local for scope control purposes; used by
+ * FaceStatus
+ *
+ * @see <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">FaceMgmt</a>
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public enum FaceScope {
+
+  NONE(-1), // invalid value
+
+  NON_LOCAL(0),
+  LOCAL(1);
+
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  FaceScope(int value) {
+    this.value = value;
+  }
+
+  public final int toInteger() {
+    return value;
+  }
+
+  public static FaceScope
+  fromInteger(int value) {
+    switch (value) {
+      case 0:
+        return NON_LOCAL;
+      case 1:
+        return LOCAL;
+      default:
+        return NONE;
+    }
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/enums/LinkType.java b/src/main/java/com/intel/jndn/management/enums/LinkType.java
new file mode 100644
index 0000000..99da791
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/LinkType.java
@@ -0,0 +1,57 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.enums;
+
+/**
+ * NFD face link type
+ * @see <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">FaceMgmt</a>
+ */
+public enum LinkType {
+
+  NONE(-1), // invalid value
+
+  /**
+   * Link is point-to-point
+   */
+  POINT_TO_POINT(0),
+
+  /**
+   * Link is multi-access
+   */
+  MULTI_ACCESS(1);
+
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  LinkType(int value) {
+    this.value = value;
+  }
+
+  public final int toInteger() {
+    return value;
+  }
+
+  public static LinkType
+  fromInteger(int value) {
+    switch (value) {
+      case 0:
+        return POINT_TO_POINT;
+      case 1:
+        return MULTI_ACCESS;
+      default:
+        return NONE;
+    }
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/types/LocalControlHeader.java b/src/main/java/com/intel/jndn/management/enums/LocalControlHeader.java
similarity index 81%
rename from src/main/java/com/intel/jndn/management/types/LocalControlHeader.java
rename to src/main/java/com/intel/jndn/management/enums/LocalControlHeader.java
index 5ca393f..86a205d 100644
--- a/src/main/java/com/intel/jndn/management/types/LocalControlHeader.java
+++ b/src/main/java/com/intel/jndn/management/enums/LocalControlHeader.java
@@ -11,7 +11,7 @@
  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  * more details.
  */
-package com.intel.jndn.management.types;
+package com.intel.jndn.management.enums;
 
 /**
  * Define constants for local control header options. See
@@ -25,12 +25,15 @@
   NEXT_HOP_FACE_ID(2),
   CACHING_POLICY(3);
 
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
   LocalControlHeader(int value) {
-    value_ = value;
+    this.value = value;
   }
 
-  public final int getNumericValue() {
-    return value_;
+  public final int toInteger() {
+    return value;
   }
-  private final int value_;
 }
diff --git a/src/main/java/com/intel/jndn/management/enums/NfdTlv.java b/src/main/java/com/intel/jndn/management/enums/NfdTlv.java
new file mode 100644
index 0000000..d414ad2
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/NfdTlv.java
@@ -0,0 +1,94 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+
+package com.intel.jndn.management.enums;
+
+/**
+ * NFD Management protocol TLV codes
+ */
+public class NfdTlv {
+  /**
+   * Prevent creation of NfdTlv instances
+   */
+  private NfdTlv() {
+  }
+
+  // ControlParameters
+  // http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+  public static final int ControlParameters   = 104;
+  public static final int FaceId              = 105;
+  public static final int Uri                 = 114;
+  public static final int LocalControlFeature = 110;
+  public static final int Origin              = 111;
+  public static final int Cost                = 106;
+  public static final int Flags               = 108;
+  public static final int Strategy            = 107;
+  public static final int ExpirationPeriod    = 109;
+
+  // ControlResponse
+  // http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+  public static final int ControlResponse = 101;
+  public static final int StatusCode      = 102;
+  public static final int StatusText      = 103;
+
+  // ForwarderStatus
+  // http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus
+  public static final int NfdVersion           = 128;
+  public static final int StartTimestamp       = 129;
+  public static final int CurrentTimestamp     = 130;
+  public static final int NNameTreeEntries     = 131;
+  public static final int NFibEntries          = 132;
+  public static final int NPitEntries          = 133;
+  public static final int NMeasurementsEntries = 134;
+  public static final int NCsEntries           = 135;
+
+  // Face Management
+  // http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt
+  public static final int FaceStatus            = 128;
+  public static final int LocalUri              = 129;
+  public static final int ChannelStatus         = 130;
+  public static final int UriScheme             = 131;
+  public static final int FaceScope             = 132;
+  public static final int FacePersistency       = 133;
+  public static final int LinkType              = 134;
+  public static final int FaceQueryFilter       = 150;
+  public static final int FaceEventNotification = 192;
+  public static final int FaceEventKind         = 193;
+
+  // ForwarderStatus and FaceStatus counters
+  // http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus
+  // http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt
+  public static final int NInInterests  = 144;
+  public static final int NInDatas      = 145;
+  public static final int NInNacks      = 151;
+  public static final int NOutInterests = 146;
+  public static final int NOutDatas     = 147;
+  public static final int NOutNacks     = 152;
+  public static final int NInBytes      = 148;
+  public static final int NOutBytes     = 149;
+
+  // FIB Management
+  // http://redmine.named-data.net/projects/nfd/wiki/FibMgmt
+  public static final int FibEntry      = 128;
+  public static final int NextHopRecord = 129;
+
+  // Strategy Choice Management
+  // http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice
+  public static final int StrategyChoice = 128;
+
+  // RIB Management
+  // http://redmine.named-data.net/projects/nfd/wiki/RibMgmt
+  public static final int RibEntry = 128;
+  public static final int Route    = 129;
+}
diff --git a/src/main/java/com/intel/jndn/management/enums/RouteFlags.java b/src/main/java/com/intel/jndn/management/enums/RouteFlags.java
new file mode 100644
index 0000000..334a726
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/RouteFlags.java
@@ -0,0 +1,36 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.enums;
+
+/**
+ * NFD route flags
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">RibMgmt</a>
+ */
+public enum RouteFlags {
+  NONE          (0),
+  CHILD_INHERIT (1),
+  CAPTURE       (2);
+
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  RouteFlags(int value) {
+    this.value = value;
+  }
+
+  public final int toInteger() {
+    return value;
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/enums/RouteOrigin.java b/src/main/java/com/intel/jndn/management/enums/RouteOrigin.java
new file mode 100644
index 0000000..6156276
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/enums/RouteOrigin.java
@@ -0,0 +1,60 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.enums;
+
+/**
+ * NFD route origin
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">RibMgmt</a>
+ */
+public enum RouteOrigin {
+  UNKNOWN  (-1),
+  APP      (0),
+  AUTOREG  (64),
+  CLIENT   (65),
+  AUTOCONF (66),
+  NLSR     (128),
+  STATIC   (255);
+
+  private final int value;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  RouteOrigin(int value) {
+    this.value = value;
+  }
+
+  public final int toInteger() {
+    return value;
+  }
+
+  public static RouteOrigin
+  fromInteger(int value) {
+    switch (value) {
+      case 0:
+        return APP;
+      case 64:
+        return AUTOREG;
+      case 65:
+        return CLIENT;
+      case 66:
+        return AUTOCONF;
+      case 128:
+        return NLSR;
+      case 255:
+        return STATIC;
+      default:
+        return UNKNOWN;
+    }
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/Strategies.java b/src/main/java/com/intel/jndn/management/enums/Strategies.java
similarity index 92%
rename from src/main/java/com/intel/jndn/management/Strategies.java
rename to src/main/java/com/intel/jndn/management/enums/Strategies.java
index f8816be..f1a9908 100644
--- a/src/main/java/com/intel/jndn/management/Strategies.java
+++ b/src/main/java/com/intel/jndn/management/enums/Strategies.java
@@ -1,6 +1,6 @@
 /*
  * jndn-management
- * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2015-2016, Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU Lesser General Public License,
@@ -11,7 +11,7 @@
  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  * more details.
  */
-package com.intel.jndn.management;
+package com.intel.jndn.management.enums;
 
 import net.named_data.jndn.Name;
 
diff --git a/src/main/java/com/intel/jndn/management/EncodingHelper.java b/src/main/java/com/intel/jndn/management/helpers/EncodingHelper.java
similarity index 94%
rename from src/main/java/com/intel/jndn/management/EncodingHelper.java
rename to src/main/java/com/intel/jndn/management/helpers/EncodingHelper.java
index b24241d..66e2c80 100644
--- a/src/main/java/com/intel/jndn/management/EncodingHelper.java
+++ b/src/main/java/com/intel/jndn/management/helpers/EncodingHelper.java
@@ -1,6 +1,6 @@
 /*
  * jndn-management
- * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2015-2016, Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU Lesser General Public License,
@@ -11,9 +11,11 @@
  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  * more details.
  */
-package com.intel.jndn.management;
+package com.intel.jndn.management.helpers;
 
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
 import net.named_data.jndn.ControlParameters;
 import net.named_data.jndn.ForwardingFlags;
 import net.named_data.jndn.Name;
@@ -196,4 +198,14 @@
 
     encoder.writeTypeAndLength(Tlv.ControlParameters_ControlParameters, encoder.getLength() - saveLength);
   }
+
+  /**
+   * Convert ByteBuffer to string, assuming UTF-8 encoding in the buffer
+   */
+  public static String
+  toString(ByteBuffer buffer) {
+    byte[] array = new byte[buffer.remaining()];
+    buffer.get(array);
+    return new String(array, Charset.forName("UTF-8"));
+  }
 }
diff --git a/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java b/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java
new file mode 100644
index 0000000..94e6863
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java
@@ -0,0 +1,195 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.helpers;
+
+import net.named_data.jndn.*;
+import net.named_data.jndn.encoding.EncodingException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FetchHelper implements OnData, OnTimeout {
+  public static final long DEFAULT_TIMEOUT = 2000;
+
+  private static final Logger LOG = Logger.getLogger(FetchHelper.class.getName());
+
+  private State state;
+  private Face face;
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Prevent creation of FetchHelper instances: use getData or getSegmentedData
+   */
+  private FetchHelper()
+  {
+  }
+
+  /**
+   * Private constructor: use getData or getSegmentedData
+   */
+  private FetchHelper(Face face)
+  {
+    this.face = face;
+  }
+
+  /**
+   * Get a single Data packet
+   * @param face Face instance
+   * @param interest Interest to retrieve Data
+   * @return Data packet
+   * @throws IOException if failed to retrieve packet, e.g., timeout occured
+   */
+  public static Data
+  getData(Face face, Interest interest) throws IOException {
+    FetchHelper fetcher = new FetchHelper(face);
+    return fetcher.getData(interest);
+  }
+
+  /**
+   * Get data using the exact name (without implicit digest)
+   * @param face Face instance
+   * @param name Exact name of the data packet to retrieve
+   *
+   * TODO: Allow authentication of retrieved data packets
+   */
+  public static Data
+  getData(Face face, Name name) throws IOException {
+    FetchHelper fetcher = new FetchHelper(face);
+
+    Interest interest = new Interest(name);
+    interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+    interest.setMustBeFresh(false); // this is bug in jndn
+    // interest.setMinSuffixComponents(1); // implicit digest
+    // interest.setMaxSuffixComponents(1); // implicit digest
+    return fetcher.getData(interest);
+  }
+
+  /**
+   * Get concatenated data from the segmented
+   * @param face Face instance
+   * @param prefix Prefix of the retrieved data. The retrieved data must have version and segment numbers after this
+   *               prefix
+   *
+   * Note that this method will first send interest with MustBeFresh selector to discover "latest" version of the
+   *               stream and then retrieve the rest of the stream
+   *
+   * TODO: Allow authentication of retrieved data packets
+   */
+  public static List<Data>
+  getSegmentedData(Face face, Name prefix) throws IOException {
+    FetchHelper fetcher = new FetchHelper(face);
+
+    Interest interest = new Interest(new Name(prefix));
+    interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+    interest.setMustBeFresh(true);
+    interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
+    // interest.setMinSuffixComponents(3); // version, segment, implicit digest
+    // interest.setMaxSuffixComponents(3); // version, segment, implicit digest
+
+    Data data = fetcher.getData(interest);
+
+    try {
+      data.getName().get(-1).toSegment();
+    }
+    catch (EncodingException e) {
+      throw new IOException("Retrieved data does not have segment number as the last name component", e);
+    }
+    if (data.getName().size() != prefix.size() + 2) {
+      throw new IOException("Retrieved data is not part of segmented stream");
+    }
+
+    long finalBlockId = 0;
+    try {
+      finalBlockId = data.getMetaInfo().getFinalBlockId().toSegment();
+    }
+    catch (EncodingException e) {
+      throw new IOException("Requested segmented stream is unbounded", e);
+    }
+
+    List<Data> segments = new ArrayList<>();
+    segments.add(data);
+
+    prefix.append(data.getName().get(-2));
+    for (int i = 0; i < finalBlockId; i++) {
+      interest = new Interest(new Name(prefix).appendSegment(i));
+      interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+      interest.setMustBeFresh(false);
+      // interest.setMinSuffixComponents(1); // implicit digest
+      // interest.setMaxSuffixComponents(1); // implicit digest
+      segments.add(fetcher.getData(interest));
+    }
+
+    return segments;
+  }
+
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  private Data getData(Interest interest) throws IOException {
+    this.state = new State();
+    this.face.expressInterest(interest, this, this);
+
+    while (!state.isDone) {
+      try {
+        face.processEvents();
+      } catch (EncodingException e) {
+        LOG.log(Level.INFO, "Decoding error: " + e.getMessage(), e);
+      }
+      try {
+        Thread.sleep(20);
+      } catch (InterruptedException e) {
+        // ok
+      }
+    }
+
+    if (state.response == null) {
+      throw new IOException("Communication with NFD failed");
+    }
+
+    return state.response;
+  }
+
+  @Override
+  public void onData(Interest interest, Data data) {
+    state.response = data;
+    state.isDone = true;
+  }
+
+  @Override
+  public void onTimeout(Interest interest) {
+    state.nRetries--;
+    if (state.nRetries > 0) {
+      try {
+        face.expressInterest(new Interest(interest).setNonce(null), this, this);
+      } catch (IOException e) {
+        LOG.log(Level.INFO, "Error while expressing interest: " + e.toString(), e);
+      }
+    }
+    else {
+      state.isDone = true;
+    }
+  }
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  private static class State {
+    public int nRetries = 3;
+    public Data response = null;
+    public boolean isDone = false;
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/helpers/StatusDatasetHelper.java b/src/main/java/com/intel/jndn/management/helpers/StatusDatasetHelper.java
new file mode 100644
index 0000000..0db8341
--- /dev/null
+++ b/src/main/java/com/intel/jndn/management/helpers/StatusDatasetHelper.java
@@ -0,0 +1,79 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.helpers;
+
+import com.intel.jndn.management.ManagementException;
+import com.intel.jndn.management.types.Decodable;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Helper class to handle StatusDatasets, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class StatusDatasetHelper {
+
+  /**
+   * Prevent instances of StatusDatasetHelper
+   */
+  private StatusDatasetHelper() {
+  }
+
+  public static ByteBuffer
+  combine(List<Data> segments) {
+    int size = 0;
+    for (Data segment : segments) {
+      size += segment.getContent().size();
+    }
+    ByteBuffer payloadBuffer = ByteBuffer.allocate(size);
+    for (Data segment : segments) {
+      payloadBuffer.put(segment.getContent().getImmutableArray());
+    }
+    payloadBuffer.flip();
+
+    return payloadBuffer;
+  }
+
+  /**
+   * Decode multiple status entries as part of a StatusDatasetHelper, see
+   * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+   */
+  public static final <T extends Decodable> List<T>
+  wireDecode(List<Data> segments, Class<T> type) throws ManagementException {
+    Blob payload = new Blob(combine(segments), false);
+
+    List<T> entries = new ArrayList<>();
+    int endOffset = payload.size();
+    TlvDecoder decoder = new TlvDecoder(payload.buf());
+    while (decoder.getOffset() < endOffset) {
+      try {
+        T entry = type.newInstance();
+        entry.wireDecode(decoder);
+        entries.add(entry);
+      } catch (IllegalAccessException|InstantiationException|EncodingException e) {
+        throw new ManagementException("Failed to read status dataset.", e);
+      }
+    }
+    return entries;
+  }
+}
diff --git a/src/main/java/com/intel/jndn/management/types/ControlResponse.java b/src/main/java/com/intel/jndn/management/types/ControlResponse.java
index 5514244..0d8404f 100644
--- a/src/main/java/com/intel/jndn/management/types/ControlResponse.java
+++ b/src/main/java/com/intel/jndn/management/types/ControlResponse.java
@@ -13,7 +13,7 @@
  */
 package com.intel.jndn.management.types;
 
-import com.intel.jndn.management.EncodingHelper;
+import com.intel.jndn.management.helpers.EncodingHelper;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/main/java/com/intel/jndn/management/types/Decodable.java b/src/main/java/com/intel/jndn/management/types/Decodable.java
index aec50dd..05797aa 100644
--- a/src/main/java/com/intel/jndn/management/types/Decodable.java
+++ b/src/main/java/com/intel/jndn/management/types/Decodable.java
@@ -17,12 +17,11 @@
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
 
 /**
- * Interface used by StatusDataset to decode generic message types; if they are
- * Decodable, then StatusDataset will instantiate and decode them.
+ * Interface used by StatusDatasetHelper to decode generic message types; if they are
+ * Decodable, then StatusDatasetHelper will instantiate and decode them.
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public interface Decodable {
-
-  public void wireDecode(TlvDecoder decoder) throws EncodingException;
+  void wireDecode(TlvDecoder decoder) throws EncodingException;
 }
diff --git a/src/main/java/com/intel/jndn/management/types/FacePersistency.java b/src/main/java/com/intel/jndn/management/types/FacePersistency.java
deleted file mode 100644
index d294fcb..0000000
--- a/src/main/java/com/intel/jndn/management/types/FacePersistency.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management.types;
-
-/**
- * Indicate whether the face is persistent; used by FaceStatus. See
- * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public enum FacePersistency {
-
-  PERSISTENT(0),
-  ON_DEMAND(1),
-  PERMANENT(2);
-
-  FacePersistency(int value) {
-    value_ = value;
-  }
-
-  public final int getNumericValue() {
-    return value_;
-  }
-  private final int value_;
-}
diff --git a/src/main/java/com/intel/jndn/management/types/FaceScope.java b/src/main/java/com/intel/jndn/management/types/FaceScope.java
deleted file mode 100644
index bcba855..0000000
--- a/src/main/java/com/intel/jndn/management/types/FaceScope.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management.types;
-
-/**
- * Indicate whether the face is local for scope control purposes; used by
- * FaceStatus See
- * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
- *
- * @author andrew
- */
-public enum FaceScope {
-
-  LOCAL(0),
-  NON_LOCAL(1);
-
-  FaceScope(int value) {
-    value_ = value;
-  }
-
-  public final int getNumericValue() {
-    return value_;
-  }
-  private final int value_;
-}
diff --git a/src/main/java/com/intel/jndn/management/types/FaceStatus.java b/src/main/java/com/intel/jndn/management/types/FaceStatus.java
index 88dceaf..abceaca 100644
--- a/src/main/java/com/intel/jndn/management/types/FaceStatus.java
+++ b/src/main/java/com/intel/jndn/management/types/FaceStatus.java
@@ -14,44 +14,59 @@
 package com.intel.jndn.management.types;
 
 import java.nio.ByteBuffer;
+import com.intel.jndn.management.enums.NfdTlv;
+import com.intel.jndn.management.enums.FacePersistency;
+import com.intel.jndn.management.enums.FaceScope;
+import com.intel.jndn.management.enums.LinkType;
+import com.intel.jndn.management.helpers.EncodingHelper;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
 import net.named_data.jndn.encoding.tlv.TlvEncoder;
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a FaceStatus object from /localhost/nfd/faces/list; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt</a>
- * for details
+ * Represent a FaceStatus object from /localhost/nfd/faces/list
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt">Face Management</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class FaceStatus implements Decodable {
+  private int faceId = 0;
+  private String remoteUri = "";
+  private String localUri = "";
+  private FaceScope faceScope = FaceScope.LOCAL;
+  private FacePersistency facePersistency = FacePersistency.PERSISTENT;
+  private LinkType linkType = LinkType.POINT_TO_POINT;
+
+  private int expirationPeriod = 0;
+  private int inInterests = 0;
+  private int inDatas = 0;
+  private int inNacks = 0;
+
+  private int outInterests = 0;
+  private int outDatas = 0;
+  private int outNacks = 0;
+
+  private int inBytes = 0;
+  private int outBytes = 0;
+
+  /////////////////////////////////////////////////////////////////////////////
 
   /**
-   * Spec from
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand</a>
+   * Default constructor
    */
-  public static final int TLV_FACE_ID = 105;
-  public static final int TLV_URI = 114;
-  public static final int TLV_EXPIRATION_PERIOD = 109;
+  public FaceStatus() {
+    // nothing to do
+  }
 
   /**
-   * Spec from
-   * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
    */
-  public static final int TLV_FACE_STATUS = 128;
-  public static final int TLV_LOCAL_URI = 129;
-  public static final int TLV_CHANNEL_STATUS = 130;
-  public static final int TLV_FACE_SCOPE = 132;
-  public static final int TLV_FACE_PERSISTENCY = 133;
-  public static final int TLV_LINK_TYPE = 134;
-  public static final int TLV_N_IN_INTERESTS = 144;
-  public static final int TLV_N_IN_DATAS = 145;
-  public static final int TLV_N_OUT_INTERESTS = 146;
-  public static final int TLV_N_OUT_DATAS = 147;
-  public static final int TLV_N_IN_BYTES = 148;
-  public static final int TLV_N_OUT_BYTES = 149;
+  public FaceStatus(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -65,33 +80,38 @@
   }
 
   /**
-   * Encode as part of an existing encode context.
-   *
-   * @param encoder
+   * Encode as part of an existing encode context
    */
   public final void wireEncode(TlvEncoder encoder) {
     int saveLength = encoder.getLength();
-    encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_BYTES, outBytes);
-    encoder.writeNonNegativeIntegerTlv(TLV_N_IN_BYTES, inBytes);
-    encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_DATAS, outDatas);
-    encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_INTERESTS, outInterests);
-    encoder.writeNonNegativeIntegerTlv(TLV_N_IN_DATAS, inDatas);
-    encoder.writeNonNegativeIntegerTlv(TLV_N_IN_INTERESTS, inInterests);
-    encoder.writeNonNegativeIntegerTlv(TLV_LINK_TYPE, linkType.getNumericValue());
-    encoder.writeNonNegativeIntegerTlv(TLV_FACE_PERSISTENCY, facePersistency.getNumericValue());
-    encoder.writeNonNegativeIntegerTlv(TLV_FACE_SCOPE, faceScope.getNumericValue());
-    encoder.writeOptionalNonNegativeIntegerTlv(TLV_EXPIRATION_PERIOD, expirationPeriod);
-    encoder.writeBlobTlv(TLV_LOCAL_URI, new Blob(localUri).buf());
-    encoder.writeBlobTlv(TLV_URI, new Blob(uri).buf());
-    encoder.writeNonNegativeIntegerTlv(TLV_FACE_ID, faceId);
-    encoder.writeTypeAndLength(TLV_FACE_STATUS, encoder.getLength() - saveLength);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutBytes, outBytes);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInBytes, inBytes);
+
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutNacks, outNacks);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutDatas, outDatas);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutInterests, outInterests);
+
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInNacks, inNacks);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInDatas, inDatas);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInInterests, inInterests);
+
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.LinkType, linkType.toInteger());
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.FacePersistency, facePersistency.toInteger());
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.FaceScope, faceScope.toInteger());
+
+    encoder.writeOptionalNonNegativeIntegerTlv(NfdTlv.ExpirationPeriod, expirationPeriod);
+    encoder.writeBlobTlv(NfdTlv.LocalUri, new Blob(localUri).buf());
+    encoder.writeBlobTlv(NfdTlv.Uri, new Blob(remoteUri).buf());
+
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.FaceId, faceId);
+    encoder.writeTypeAndLength(NfdTlv.FaceStatus, encoder.getLength() - saveLength);
   }
 
   /**
    * Decode the input from its TLV format.
    *
    * @param input The input buffer to decode. This reads from position() to
-   * limit(), but does not change the position.
+   *              limit(), but does not change the position.
    * @throws net.named_data.jndn.encoding.EncodingException
    */
   public final void wireDecode(ByteBuffer input) throws EncodingException {
@@ -100,37 +120,38 @@
   }
 
   /**
-   * Decode as part of an existing decode context.
-   *
-   * @param decoder
-   * @throws EncodingException
+   * Decode as part of an existing decode context
    */
   @Override
   public void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_FACE_STATUS);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.FaceStatus);
     // parse
-    this.faceId = (int) decoder.readNonNegativeIntegerTlv(TLV_FACE_ID);
-    Blob uri_ = new Blob(decoder.readBlobTlv(TLV_URI), true); // copy because buffer is immutable
-    this.uri = uri_.toString();
-    Blob localUri_ = new Blob(decoder.readBlobTlv(TLV_LOCAL_URI), true); // copy because buffer is immutable
-    this.localUri = localUri_.toString();
-    this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(TLV_EXPIRATION_PERIOD, endOffset);
-    this.faceScope = FaceScope.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_FACE_SCOPE)];
-    this.facePersistency = FacePersistency.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_FACE_PERSISTENCY)];
-    this.linkType = LinkType.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_LINK_TYPE)];
-    this.inInterests = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_INTERESTS);
-    this.inDatas = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_DATAS);
-    this.outInterests = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_INTERESTS);
-    this.outDatas = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_DATAS);
-    this.inBytes = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_BYTES);
-    this.outBytes = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_BYTES);
+    this.faceId = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.FaceId);
+
+    this.remoteUri = EncodingHelper.toString(decoder.readBlobTlv(NfdTlv.Uri));
+    this.localUri = EncodingHelper.toString(decoder.readBlobTlv(NfdTlv.LocalUri));
+
+    this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(NfdTlv.ExpirationPeriod, endOffset);
+    this.faceScope = FaceScope.fromInteger((int) decoder.readNonNegativeIntegerTlv(NfdTlv.FaceScope));
+    this.facePersistency = FacePersistency.fromInteger((int) decoder.readNonNegativeIntegerTlv(NfdTlv.FacePersistency));
+    this.linkType = LinkType.fromInteger((int) decoder.readNonNegativeIntegerTlv(NfdTlv.LinkType));
+
+    this.inInterests = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NInInterests);
+    this.inDatas = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NInDatas);
+    this.inNacks = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NInNacks);
+
+    this.outInterests = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NOutInterests);
+    this.outDatas = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NOutDatas);
+    this.outNacks = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NOutNacks);
+
+    this.inBytes = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NInBytes);
+    this.outBytes = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.NOutBytes);
+
     decoder.finishNestedTlvs(endOffset);
   }
 
   /**
    * Get face ID
-   *
-   * @return
    */
   public int getFaceId() {
     return faceId;
@@ -138,35 +159,29 @@
 
   /**
    * Set face ID
-   *
-   * @param faceId
    */
-  public void setFaceId(int faceId) {
+  public FaceStatus setFaceId(int faceId) {
     this.faceId = faceId;
+    return this;
   }
 
   /**
    * Get face ID
-   *
-   * @return
    */
-  public String getUri() {
-    return uri;
+  public String getRemoteUri() {
+    return remoteUri;
   }
 
   /**
    * Set URI
-   *
-   * @param uri
    */
-  public void setUri(String uri) {
-    this.uri = uri;
+  public FaceStatus setRemoteUri(String uri) {
+    this.remoteUri = uri;
+    return this;
   }
 
   /**
    * Get face ID
-   *
-   * @return
    */
   public String getLocalUri() {
     return localUri;
@@ -174,17 +189,21 @@
 
   /**
    * Set local URI
-   *
-   * @param localUri
    */
-  public void setLocalUri(String localUri) {
+  public FaceStatus setLocalUri(String localUri) {
     this.localUri = localUri;
+    return this;
+  }
+
+  /**
+   * Check if Face has expiration period set
+   */
+  public boolean hasExpirationPeriod() {
+    return expirationPeriod > 0;
   }
 
   /**
    * Get expiration period
-   *
-   * @return
    */
   public int getExpirationPeriod() {
     return expirationPeriod;
@@ -192,17 +211,14 @@
 
   /**
    * Set expiration period
-   *
-   * @param expirationPeriod
    */
-  public void setExpirationPeriod(int expirationPeriod) {
+  public FaceStatus setExpirationPeriod(int expirationPeriod) {
     this.expirationPeriod = expirationPeriod;
+    return this;
   }
 
   /**
    * Get face scope value
-   *
-   * @return
    */
   public FaceScope getFaceScope() {
     return faceScope;
@@ -210,17 +226,14 @@
 
   /**
    * Set face scope value
-   *
-   * @param faceScope
    */
-  public void setFaceScope(FaceScope faceScope) {
+  public FaceStatus setFaceScope(FaceScope faceScope) {
     this.faceScope = faceScope;
+    return this;
   }
 
   /**
    * Get face persistency value
-   *
-   * @return
    */
   public FacePersistency getFacePersistency() {
     return facePersistency;
@@ -228,17 +241,14 @@
 
   /**
    * Set face persistency value
-   *
-   * @param facePersistency
    */
-  public void setFacePersistency(FacePersistency facePersistency) {
+  public FaceStatus setFacePersistency(FacePersistency facePersistency) {
     this.facePersistency = facePersistency;
+    return this;
   }
 
   /**
    * Get link type
-   *
-   * @return
    */
   public LinkType getLinkType() {
     return linkType;
@@ -246,132 +256,130 @@
 
   /**
    * Set link type
-   *
-   * @param linkType
    */
-  public void setLinkType(LinkType linkType) {
+  public FaceStatus setLinkType(LinkType linkType) {
     this.linkType = linkType;
+    return this;
   }
 
   /**
    * Get number of received Interest packets
-   *
-   * @return
    */
-  public int getInInterests() {
+  public int getNInInterests() {
     return inInterests;
   }
 
   /**
    * Set number of received Interest packets
-   *
-   * @param inInterests
    */
-  public void setInInterests(int inInterests) {
+  public FaceStatus setNInInterests(int inInterests) {
     this.inInterests = inInterests;
+    return this;
   }
 
   /**
    * Get number of sent Interest packets
-   *
-   * @return
    */
-  public int getOutInterests() {
+  public int getNOutInterests() {
     return outInterests;
   }
 
   /**
    * Set number of sent Interest packets
-   *
-   * @param outInterests
    */
-  public void setOutInterests(int outInterests) {
+  public FaceStatus setNOutInterests(int outInterests) {
     this.outInterests = outInterests;
+    return this;
   }
 
   /**
    * Get number of received Data packets
-   *
-   * @return
    */
-  public int getInDatas() {
+  public int getNInDatas() {
     return inDatas;
   }
 
   /**
    * Set number of received Data packets
-   *
-   * @param inDatas
    */
-  public void setInDatas(int inDatas) {
+  public FaceStatus setNInDatas(int inDatas) {
     this.inDatas = inDatas;
+    return this;
   }
 
   /**
    * Get number of sent Data packets
-   *
-   * @return
    */
-  public int getOutDatas() {
+  public int getNOutDatas() {
     return outDatas;
   }
 
   /**
    * Set number of sent Data packets
-   *
-   * @param outDatas
    */
-  public void setOutDatas(int outDatas) {
+  public FaceStatus setNOutDatas(int outDatas) {
     this.outDatas = outDatas;
+    return this;
   }
 
   /**
-   * Get number of input bytes
-   *
-   * @return
+   * Get number of received Data packets
    */
-  public int getInBytes() {
+  public int getNInNacks() {
+    return inNacks;
+  }
+
+  /**
+   * Set number of received Data packets
+   */
+  public FaceStatus setNInNacks(int inNacks) {
+    this.inNacks = inNacks;
+    return this;
+  }
+
+  /**
+   * Get number of sent Data packets
+   */
+  public int getNOutNacks() {
+    return outNacks;
+  }
+
+  /**
+   * Set number of sent Data packets
+   */
+  public FaceStatus setNOutNacks(int outNacks) {
+    this.outNacks = outNacks;
+    return this;
+  }
+
+
+  /**
+   * Get number of input bytes
+   */
+  public int getNInBytes() {
     return inBytes;
   }
 
   /**
    * Set number of input bytes
-   *
-   * @param inBytes
    */
-  public void setInBytes(int inBytes) {
+  public FaceStatus setNInBytes(int inBytes) {
     this.inBytes = inBytes;
+    return this;
   }
 
   /**
    * Get number of output bytes
-   *
-   * @return
    */
-  public int getOutBytes() {
+  public int getNOutBytes() {
     return outBytes;
   }
 
   /**
    * Set number of output bytes
-   *
-   * @param outBytes
    */
-  public void setOutBytes(int outBytes) {
+  public FaceStatus setNOutBytes(int outBytes) {
     this.outBytes = outBytes;
+    return this;
   }
-
-  private int faceId = -1;
-  private String uri = ""; // can't use URI because some are invalid syntax
-  private String localUri = ""; // can't use URI because some are invalid syntax
-  private int expirationPeriod = 0;
-  private FaceScope faceScope = FaceScope.LOCAL;
-  private FacePersistency facePersistency = FacePersistency.ON_DEMAND;
-  private LinkType linkType = LinkType.POINT_TO_POINT;
-  private int inInterests = 0;
-  private int outInterests = 0;
-  private int inDatas = 0;
-  private int outDatas = 0;
-  private int inBytes = 0;
-  private int outBytes = 0;
 }
diff --git a/src/main/java/com/intel/jndn/management/types/FibEntry.java b/src/main/java/com/intel/jndn/management/types/FibEntry.java
index 8c74636..e30e9a9 100644
--- a/src/main/java/com/intel/jndn/management/types/FibEntry.java
+++ b/src/main/java/com/intel/jndn/management/types/FibEntry.java
@@ -13,10 +13,13 @@
  */
 package com.intel.jndn.management.types;
 
-import com.intel.jndn.management.EncodingHelper;
+import com.intel.jndn.management.helpers.EncodingHelper;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
+
+import com.intel.jndn.management.enums.NfdTlv;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
@@ -24,14 +27,32 @@
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a FibEntry returned from /localhost/nfd/fib/list; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset</a>
+ * Represent a FibEntry returned from /localhost/nfd/fib/list
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">FIB Dataset</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class FibEntry implements Decodable {
+  private Name name = new Name();
+  private List<NextHopRecord> records = new ArrayList<>();
 
-  public final static int TLV_FIB_ENTRY = 128;
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Default constructor
+   */
+  public FibEntry() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public FibEntry(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -45,24 +66,23 @@
   }
 
   /**
-   * Encode as part of an existing encode context.
-   *
-   * @param encoder
+   * Encode as part of an existing encode context
    */
   public final void wireEncode(TlvEncoder encoder) {
     int saveLength = encoder.getLength();
-    for (NextHopRecord record : records) {
-      record.wireEncode(encoder);
+    ListIterator<NextHopRecord> nh = records.listIterator(records.size());
+    while (nh.hasPrevious()) {
+      nh.previous().wireEncode(encoder);
     }
     EncodingHelper.encodeName(name, encoder);
-    encoder.writeTypeAndLength(TLV_FIB_ENTRY, encoder.getLength() - saveLength);
+    encoder.writeTypeAndLength(NfdTlv.FibEntry, encoder.getLength() - saveLength);
   }
 
   /**
    * Decode the input from its TLV format.
    *
    * @param input The input buffer to decode. This reads from position() to
-   * limit(), but does not change the position.
+   *              limit(), but does not change the position.
    * @throws EncodingException For invalid encoding.
    */
   public final void wireDecode(ByteBuffer input) throws EncodingException {
@@ -71,14 +91,11 @@
   }
 
   /**
-   * Decode as part of an existing decode context.
-   *
-   * @param decoder
-   * @throws EncodingException
+   * Decode as part of an existing decode context
    */
   @Override
   public final void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_FIB_ENTRY);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.FibEntry);
     name = EncodingHelper.decodeName(decoder);
     while (decoder.getOffset() < endOffset) {
       NextHopRecord record = new NextHopRecord();
@@ -90,40 +107,36 @@
 
   /**
    * Get name
-   *
-   * @return
    */
-  public Name getName() {
+  public Name getPrefix() {
     return name;
   }
 
   /**
    * Set name
-   *
-   * @param name
    */
-  public void setName(Name name) {
+  public FibEntry setPrefix(Name name) {
     this.name = name;
+    return this;
   }
 
   /**
    * Get records
-   *
-   * @return
    */
-  public List<NextHopRecord> getRecords() {
+  public List<NextHopRecord> getNextHopRecords() {
     return records;
   }
 
   /**
    * Set records
-   *
-   * @param records
    */
-  public void setRecords(List<NextHopRecord> records) {
+  public FibEntry setNextHopRecords(List<NextHopRecord> records) {
     this.records = records;
+    return this;
   }
 
-  private Name name = new Name();
-  private List<NextHopRecord> records = new ArrayList<>();
+  public FibEntry addNextHopRecord(NextHopRecord record) {
+    this.records.add(record);
+    return this;
+  }
 }
diff --git a/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java b/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java
index 88ecc35..7ad4712 100644
--- a/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java
+++ b/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java
@@ -14,31 +14,52 @@
 package com.intel.jndn.management.types;
 
 import java.nio.ByteBuffer;
+
+import com.intel.jndn.management.enums.NfdTlv;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
 import net.named_data.jndn.encoding.tlv.TlvEncoder;
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a ForwarderStatus object from
- * http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus.
+ * Represent a ForwarderStatus object
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus">ForwarderStatus</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class ForwarderStatus implements Decodable {
+  private String nfdVersion = "";
+  private long startTimestamp = 0;
+  private long currentTimestamp = 0;
+  private long nNameTreeEntries = 0;
+  private long nFibEntries = 0;
+  private long nPitEntries = 0;
+  private long nMeasurementEntries = 0;
+  private long nCsEntries = 0;
+  private long nInInterests = 0;
+  private long nInDatas = 0;
+  private long nInNacks = 0;
+  private long nOutInterests = 0;
+  private long nOutDatas = 0;
+  private long nOutNacks = 0;
 
-  public static final int TLV_NFD_VERSION = 0x80;
-  public static final int TLV_START_TIMESTAMP = 0x81;
-  public static final int TLV_CURRENT_TIMESTAMP = 0x82;
-  public static final int TLV_NUM_NAME_TREE_ENTRIES = 0x83;
-  public static final int TLV_NUM_FIB_ENTRIES = 0x84;
-  public static final int TLV_NUM_PIT_ENTRIES = 0x85;
-  public static final int TLV_NUM_MEASUREMENT_ENTRIES = 0x86;
-  public static final int TLV_NUM_CS_ENTRIES = 0x87;
-  public static final int TLV_NUM_IN_INTERESTS = 0x90;
-  public static final int TLV_NUM_IN_DATAS = 0x91;
-  public static final int TLV_NUM_OUT_INTERESTS = 0x92;
-  public static final int TLV_NUM_OUT_DATAS = 0x93;
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Default constructor
+   */
+  public ForwarderStatus() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public ForwarderStatus(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -53,22 +74,22 @@
 
   /**
    * Encode as part of an existing encode context.
-   *
-   * @param encoder
    */
   public final void wireEncode(TlvEncoder encoder) {
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_DATAS, numOutDatas);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_INTERESTS, numOutInterests);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_DATAS, numInDatas);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_INTERESTS, numInInterests);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_CS_ENTRIES, numCsEntries);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_MEASUREMENT_ENTRIES, numMeasurementEntries);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_PIT_ENTRIES, numPitEntries);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_FIB_ENTRIES, numFibEntries);
-    encoder.writeNonNegativeIntegerTlv(TLV_NUM_NAME_TREE_ENTRIES, numNameTreeEntries);
-    encoder.writeNonNegativeIntegerTlv(TLV_CURRENT_TIMESTAMP, currentTimestamp);
-    encoder.writeNonNegativeIntegerTlv(TLV_START_TIMESTAMP, startTimestamp);
-    encoder.writeBlobTlv(TLV_NFD_VERSION, new Blob(nfdVersion).buf());
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutNacks, nOutNacks);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutDatas, nOutDatas);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NOutInterests, nOutInterests);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInNacks, nInNacks);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInDatas, nInDatas);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NInInterests, nInInterests);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NCsEntries, nCsEntries);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NMeasurementsEntries, nMeasurementEntries);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NPitEntries, nPitEntries);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NFibEntries, nFibEntries);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.NNameTreeEntries, nNameTreeEntries);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.CurrentTimestamp, currentTimestamp);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.StartTimestamp, startTimestamp);
+    encoder.writeBlobTlv(NfdTlv.NfdVersion, new Blob(nfdVersion).buf());
   }
 
   /**
@@ -84,25 +105,24 @@
   }
 
   /**
-   * Decode as part of an existing decode context.
-   *
-   * @param decoder
-   * @throws EncodingException
+   * Decode as part of an existing decode context
    */
   @Override
   public void wireDecode(TlvDecoder decoder) throws EncodingException {
-    this.nfdVersion = new Blob(decoder.readBlobTlv(TLV_NFD_VERSION), true).toString();
-    this.startTimestamp = decoder.readNonNegativeIntegerTlv(TLV_START_TIMESTAMP);
-    this.currentTimestamp = decoder.readNonNegativeIntegerTlv(TLV_CURRENT_TIMESTAMP);
-    this.numNameTreeEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_NAME_TREE_ENTRIES);
-    this.numFibEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_FIB_ENTRIES);
-    this.numPitEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_PIT_ENTRIES);
-    this.numMeasurementEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_MEASUREMENT_ENTRIES);
-    this.numCsEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_CS_ENTRIES);
-    this.numInInterests = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_INTERESTS);
-    this.numInDatas = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_DATAS);
-    this.numOutInterests = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_INTERESTS);
-    this.numOutDatas = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_DATAS);
+    this.nfdVersion = new Blob(decoder.readBlobTlv(NfdTlv.NfdVersion), true).toString();
+    this.startTimestamp = decoder.readNonNegativeIntegerTlv(NfdTlv.StartTimestamp);
+    this.currentTimestamp = decoder.readNonNegativeIntegerTlv(NfdTlv.CurrentTimestamp);
+    this.nNameTreeEntries = decoder.readNonNegativeIntegerTlv(NfdTlv.NNameTreeEntries);
+    this.nFibEntries = decoder.readNonNegativeIntegerTlv(NfdTlv.NFibEntries);
+    this.nPitEntries = decoder.readNonNegativeIntegerTlv(NfdTlv.NPitEntries);
+    this.nMeasurementEntries = decoder.readNonNegativeIntegerTlv(NfdTlv.NMeasurementsEntries);
+    this.nCsEntries = decoder.readNonNegativeIntegerTlv(NfdTlv.NCsEntries);
+    this.nInInterests = decoder.readNonNegativeIntegerTlv(NfdTlv.NInInterests);
+    this.nInDatas = decoder.readNonNegativeIntegerTlv(NfdTlv.NInDatas);
+    this.nInNacks = decoder.readNonNegativeIntegerTlv(NfdTlv.NInNacks);
+    this.nOutInterests = decoder.readNonNegativeIntegerTlv(NfdTlv.NOutInterests);
+    this.nOutDatas = decoder.readNonNegativeIntegerTlv(NfdTlv.NOutDatas);
+    this.nOutNacks = decoder.readNonNegativeIntegerTlv(NfdTlv.NOutNacks);
   }
 
   public String getNfdVersion() {
@@ -129,88 +149,91 @@
     this.currentTimestamp = currentTimestamp;
   }
 
-  public long getNumNameTreeEntries() {
-    return numNameTreeEntries;
+  public long getNNameTreeEntries() {
+    return nNameTreeEntries;
   }
 
-  public void setNumNameTreeEntries(long numNameTreeEntries) {
-    this.numNameTreeEntries = numNameTreeEntries;
+  public void setNNameTreeEntries(long nNameTreeEntries) {
+    this.nNameTreeEntries = nNameTreeEntries;
   }
 
-  public long getNumFibEntries() {
-    return numFibEntries;
+  public long getNFibEntries() {
+    return nFibEntries;
   }
 
-  public void setNumFibEntries(long numFibEntries) {
-    this.numFibEntries = numFibEntries;
+  public void setNFibEntries(long nFibEntries) {
+    this.nFibEntries = nFibEntries;
   }
 
-  public long getNumPitEntries() {
-    return numPitEntries;
+  public long getNPitEntries() {
+    return nPitEntries;
   }
 
-  public void setNumPitEntries(long numPitEntries) {
-    this.numPitEntries = numPitEntries;
+  public void setNPitEntries(long nPitEntries) {
+    this.nPitEntries = nPitEntries;
   }
 
-  public long getNumMeasurementEntries() {
-    return numMeasurementEntries;
+  public long getNMeasurementsEntries() {
+    return nMeasurementEntries;
   }
 
-  public void setNumMeasurementEntries(long numMeasurementEntries) {
-    this.numMeasurementEntries = numMeasurementEntries;
+  public void setNMeasurementsEntries(long nMeasurementEntries) {
+    this.nMeasurementEntries = nMeasurementEntries;
   }
 
-  public long getNumCsEntries() {
-    return numCsEntries;
+  public long getNCsEntries() {
+    return nCsEntries;
   }
 
-  public void setNumCsEntries(long numCsEntries) {
-    this.numCsEntries = numCsEntries;
+  public void setNCsEntries(long nCsEntries) {
+    this.nCsEntries = nCsEntries;
   }
 
-  public long getNumInInterests() {
-    return numInInterests;
+  public long getNInInterests() {
+    return nInInterests;
   }
 
-  public void setNumInInterests(long numInInterests) {
-    this.numInInterests = numInInterests;
+  public void setNInInterests(long nInInterests) {
+    this.nInInterests = nInInterests;
   }
 
-  public long getNumInDatas() {
-    return numInDatas;
+  public long getNInDatas() {
+    return nInDatas;
   }
 
-  public void setNumInDatas(long numInDatas) {
-    this.numInDatas = numInDatas;
+  public void setNInDatas(long nInDatas) {
+    this.nInDatas = nInDatas;
   }
 
-  public long getNumOutInterests() {
-    return numOutInterests;
+  public long getNOutInterests() {
+    return nOutInterests;
   }
 
-  public void setNumOutInterests(long numOutInterests) {
-    this.numOutInterests = numOutInterests;
+  public void setNOutInterests(long nOutInterests) {
+    this.nOutInterests = nOutInterests;
   }
 
-  public long getNumOutDatas() {
-    return numOutDatas;
+  public long getNOutDatas() {
+    return nOutDatas;
   }
 
-  public void setNumOutDatas(long numOutDatas) {
-    this.numOutDatas = numOutDatas;
+  public void setNOutDatas(long nOutDatas) {
+    this.nOutDatas = nOutDatas;
   }
 
-  private String nfdVersion = "";
-  private long startTimestamp;
-  private long currentTimestamp;
-  private long numNameTreeEntries;
-  private long numFibEntries;
-  private long numPitEntries;
-  private long numMeasurementEntries;
-  private long numCsEntries;
-  private long numInInterests;
-  private long numInDatas;
-  private long numOutInterests;
-  private long numOutDatas;
+  public long getNInNacks() {
+    return nInNacks;
+  }
+
+  public void setNInNacks(long nInNacks) {
+    this.nInNacks = nInNacks;
+  }
+
+  public long getNOutNacks() {
+    return nOutNacks;
+  }
+
+  public void setNOutNacks(long nOutNacks) {
+    this.nOutNacks = nOutNacks;
+  }
 }
diff --git a/src/main/java/com/intel/jndn/management/types/LinkType.java b/src/main/java/com/intel/jndn/management/types/LinkType.java
deleted file mode 100644
index a0f517c..0000000
--- a/src/main/java/com/intel/jndn/management/types/LinkType.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management.types;
-
-/**
- * Indicate the type of communication link; used by FaceStatus See
- * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public enum LinkType {
-
-  POINT_TO_POINT(0),
-  MULTI_ACCESS(1);
-
-  LinkType(int value) {
-    value_ = value;
-  }
-
-  public final int getNumericValue() {
-    return value_;
-  }
-  private final int value_;
-}
diff --git a/src/main/java/com/intel/jndn/management/types/NextHopRecord.java b/src/main/java/com/intel/jndn/management/types/NextHopRecord.java
index 3b40104..84756e5 100644
--- a/src/main/java/com/intel/jndn/management/types/NextHopRecord.java
+++ b/src/main/java/com/intel/jndn/management/types/NextHopRecord.java
@@ -14,6 +14,8 @@
 package com.intel.jndn.management.types;
 
 import java.nio.ByteBuffer;
+
+import com.intel.jndn.management.enums.NfdTlv;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.Tlv;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
@@ -21,19 +23,37 @@
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a NextHopRecord in a FibEntry; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset</a>
+ * Represent a NextHopRecord in a FibEntry
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">FIB Dataset</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class NextHopRecord {
+  private int faceId;
+  private int cost;
 
-  public final static int TLV_NEXT_HOP_RECORD = 129;
+  /////////////////////////////////////////////////////////////////////////////
 
   /**
-   * Encode using a new TLV encoder.
+   * Default constructor
+   */
+  public NextHopRecord() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public NextHopRecord(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
+
+  /**
+   * Encode using a new TLV encoder
    *
-   * @return The encoded buffer.
+   * @return The encoded buffer
    */
   public final Blob wireEncode() {
     TlvEncoder encoder = new TlvEncoder();
@@ -42,15 +62,13 @@
   }
 
   /**
-   * Encode as part of an existing encode context.
-   *
-   * @param encoder
+   * Encode as part of an existing encode context
    */
   public final void wireEncode(TlvEncoder encoder) {
     int saveLength = encoder.getLength();
     encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Cost, cost);
     encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId, faceId);
-    encoder.writeTypeAndLength(TLV_NEXT_HOP_RECORD, encoder.getLength() - saveLength);
+    encoder.writeTypeAndLength(NfdTlv.NextHopRecord, encoder.getLength() - saveLength);
   }
 
   /**
@@ -66,13 +84,10 @@
   }
 
   /**
-   * Decode as part of an existing decode context.
-   *
-   * @param decoder
-   * @throws EncodingException
+   * Decode as part of an existing decode context
    */
   public final void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_NEXT_HOP_RECORD);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.NextHopRecord);
     this.faceId = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId);
     this.cost = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Cost);
     decoder.finishNestedTlvs(endOffset);
@@ -80,8 +95,6 @@
 
   /**
    * Get face ID
-   *
-   * @return
    */
   public int getFaceId() {
     return faceId;
@@ -89,8 +102,6 @@
 
   /**
    * Set face ID
-   *
-   * @param faceId
    */
   public void setFaceId(int faceId) {
     this.faceId = faceId;
@@ -98,8 +109,6 @@
 
   /**
    * Get cost
-   *
-   * @return
    */
   public int getCost() {
     return cost;
@@ -107,13 +116,8 @@
 
   /**
    * Set cost
-   *
-   * @param cost
    */
   public void setCost(int cost) {
     this.cost = cost;
   }
-
-  private int faceId;
-  private int cost;
 }
diff --git a/src/main/java/com/intel/jndn/management/types/RibEntry.java b/src/main/java/com/intel/jndn/management/types/RibEntry.java
index 705352c..a5c0380 100644
--- a/src/main/java/com/intel/jndn/management/types/RibEntry.java
+++ b/src/main/java/com/intel/jndn/management/types/RibEntry.java
@@ -13,10 +13,13 @@
  */
 package com.intel.jndn.management.types;
 
-import com.intel.jndn.management.EncodingHelper;
+import com.intel.jndn.management.enums.NfdTlv;
+import com.intel.jndn.management.helpers.EncodingHelper;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.ListIterator;
+
 import net.named_data.jndn.Name;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
@@ -24,19 +27,30 @@
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a entry in the RIB; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset</a>
- * for details
+ * Represent a entry in the RIB
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">RIB Dataset</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class RibEntry implements Decodable {
+  private Name name = new Name();
+  private List<Route> routes = new ArrayList<>();
 
   /**
-   * TLV type, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments</a>
+   * Default constructor
    */
-  public final static int TLV_RIB_ENTRY = 128;
+  public RibEntry() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public RibEntry(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -51,16 +65,15 @@
 
   /**
    * Encode as part of an existing encode context.
-   *
-   * @param encoder
    */
   public final void wireEncode(TlvEncoder encoder) {
     int saveLength = encoder.getLength();
-    for (Route route : routes) {
-      route.wireEncode(encoder);
+    ListIterator<Route> route = routes.listIterator(routes.size());
+    while (route.hasPrevious()) {
+      route.previous().wireEncode(encoder);
     }
     EncodingHelper.encodeName(name, encoder);
-    encoder.writeTypeAndLength(TLV_RIB_ENTRY, encoder.getLength() - saveLength);
+    encoder.writeTypeAndLength(NfdTlv.RibEntry, encoder.getLength() - saveLength);
   }
 
   /**
@@ -77,13 +90,10 @@
 
   /**
    * Decode as part of an existing decode context.
-   *
-   * @param decoder
-   * @throws EncodingException
    */
   @Override
   public final void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_RIB_ENTRY);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.RibEntry);
     name = EncodingHelper.decodeName(decoder);
     while (decoder.getOffset() < endOffset) {
       Route route = new Route();
@@ -95,8 +105,6 @@
 
   /**
    * Get name
-   *
-   * @return
    */
   public Name getName() {
     return name;
@@ -104,8 +112,6 @@
 
   /**
    * Set name
-   *
-   * @param name
    */
   public void setName(Name name) {
     this.name = name;
@@ -113,22 +119,46 @@
 
   /**
    * Get routes
-   *
-   * @return
    */
   public List<Route> getRoutes() {
     return routes;
   }
 
   /**
+   * Add route
+   */
+  public RibEntry addRoute(Route route) {
+    getRoutes().add(route);
+    return this;
+  }
+
+  /**
+   * Clear all routes
+   */
+  public void clearRoutes() {
+    getRoutes().clear();
+  }
+
+  /**
    * Set routes
-   *
-   * @param routes
    */
   public void setRoutes(List<Route> routes) {
     this.routes = routes;
   }
 
-  private Name name = new Name();
-  private List<Route> routes = new ArrayList<>();
+  /**
+   * Get human-readable representation of RibEntry
+   */
+  @Override
+  public String toString() {
+    StringBuilder out = new StringBuilder();
+    out.append("RibEntry{\n");
+    out.append("  Name: ").append(getName().toUri()).append("\n");
+
+    for (Route route : getRoutes()) {
+      out.append("  ").append(route.toString()).append("\n");
+    }
+    out.append("}");
+    return out.toString();
+  }
 }
diff --git a/src/main/java/com/intel/jndn/management/types/Route.java b/src/main/java/com/intel/jndn/management/types/Route.java
index 15c2050..0b9acb4 100644
--- a/src/main/java/com/intel/jndn/management/types/Route.java
+++ b/src/main/java/com/intel/jndn/management/types/Route.java
@@ -13,28 +13,47 @@
  */
 package com.intel.jndn.management.types;
 
-import net.named_data.jndn.encoding.tlv.Tlv;
 import java.nio.ByteBuffer;
-import net.named_data.jndn.ForwardingFlags;
+
+import com.intel.jndn.management.enums.NfdTlv;
+import com.intel.jndn.management.enums.RouteFlags;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
 import net.named_data.jndn.encoding.tlv.TlvEncoder;
 import net.named_data.jndn.util.Blob;
 
 /**
- * Represent a Route object from /localhost/nfd/rib/list; see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset</a>
- * for details.
+ * Represent a Route object from /localhost/nfd/rib/list
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">RIB Dataset</a>
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class Route {
+  public static final int INFINITE_EXPIRATION_PERIOD = -1;
+
+  private int faceId = -1;
+  private int origin = -1;
+  private int cost = -1;
+  private int flags = RouteFlags.CHILD_INHERIT.toInteger();
+  private int expirationPeriod = INFINITE_EXPIRATION_PERIOD;
+
+  /////////////////////////////////////////////////////////////////////////////
 
   /**
-   * TLV type, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments</a>
+   * Default constructor
    */
-  public final static int TLV_ROUTE = 129;
+  public Route() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public Route(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -54,19 +73,19 @@
    */
   public final void wireEncode(TlvEncoder encoder) {
     int saveLength = encoder.getLength();
-    encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod, faceId);
-    encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Flags, flags.getNfdForwardingFlags());
-    encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Cost, cost);
-    encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Origin, origin);
-    encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId, faceId);
-    encoder.writeTypeAndLength(TLV_ROUTE, encoder.getLength() - saveLength);
+    encoder.writeOptionalNonNegativeIntegerTlv(NfdTlv.ExpirationPeriod, expirationPeriod);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.Flags, flags);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.Cost, cost);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.Origin, origin);
+    encoder.writeNonNegativeIntegerTlv(NfdTlv.FaceId, faceId);
+    encoder.writeTypeAndLength(NfdTlv.Route, encoder.getLength() - saveLength);
   }
 
   /**
    * Decode the input from its TLV format.
    *
    * @param input The input buffer to decode. This reads from position() to
-   * limit(), but does not change the position.
+   *              limit(), but does not change the position.
    * @throws net.named_data.jndn.encoding.EncodingException
    */
   public final void wireDecode(ByteBuffer input) throws EncodingException {
@@ -81,19 +100,17 @@
    * @throws EncodingException
    */
   public final void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_ROUTE);
-    this.faceId = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId);
-    this.origin = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Origin);
-    this.cost = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Cost);
-    this.flags.setNfdForwardingFlags((int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Flags));
-    this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod, endOffset);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.Route);
+    this.faceId = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.FaceId);
+    this.origin = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.Origin);
+    this.cost = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.Cost);
+    this.flags = (int) decoder.readNonNegativeIntegerTlv(NfdTlv.Flags);
+    this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(NfdTlv.ExpirationPeriod, endOffset);
     decoder.finishNestedTlvs(endOffset);
   }
 
   /**
    * Get Face ID
-   *
-   * @return
    */
   public int getFaceId() {
     return faceId;
@@ -101,17 +118,14 @@
 
   /**
    * Set Face ID
-   *
-   * @param faceId
    */
-  public void setFaceId(int faceId) {
+  public Route setFaceId(int faceId) {
     this.faceId = faceId;
+    return this;
   }
 
   /**
    * Get origin
-   *
-   * @return
    */
   public int getOrigin() {
     return origin;
@@ -119,17 +133,14 @@
 
   /**
    * Set origin
-   *
-   * @param origin
    */
-  public void setOrigin(int origin) {
+  public Route setOrigin(int origin) {
     this.origin = origin;
+    return this;
   }
 
   /**
    * Get cost
-   *
-   * @return
    */
   public int getCost() {
     return cost;
@@ -137,52 +148,68 @@
 
   /**
    * Set cost
-   *
-   * @param cost
    */
-  public void setCost(int cost) {
+  public Route setCost(int cost) {
     this.cost = cost;
+    return this;
   }
 
   /**
    * Get flags
-   *
-   * @return
    */
-  public ForwardingFlags getFlags() {
+  public int getFlags() {
     return flags;
   }
 
   /**
    * Set flags
-   *
-   * @param flags
    */
-  public void setFlags(ForwardingFlags flags) {
+  public void setFlags(int flags) {
     this.flags = flags;
   }
 
   /**
-   * Get expiration period
-   *
-   * @return
+   * Get expiration period (in milliseconds)
    */
-  public double getExpirationPeriod() {
+  public int getExpirationPeriod() {
     return expirationPeriod;
   }
 
   /**
+   * Check if route should not expire
+   */
+  public boolean hasInfiniteExpirationPeriod() {
+    return expirationPeriod < 0;
+  }
+
+  /**
    * Set expiration period
    *
    * @param expirationPeriod
    */
-  public void setExpirationPeriod(double expirationPeriod) {
+  public void setExpirationPeriod(int expirationPeriod) {
     this.expirationPeriod = expirationPeriod;
   }
 
-  private int faceId = -1;
-  private int origin = -1;
-  private int cost = -1;
-  private ForwardingFlags flags = new ForwardingFlags();
-  private double expirationPeriod = -1.0;
+  /**
+   * Get human-readable representation of Route
+   */
+  @Override
+  public String toString() {
+    StringBuilder out = new StringBuilder();
+    out.append("Route(");
+    out.append("FaceId: "); out.append(getFaceId()); out.append(", ");
+    out.append("Origin: "); out.append(getOrigin()); out.append(", ");
+    out.append("Cost: "); out.append(getCost()); out.append(", ");
+    out.append("Flags: "); out.append(getFlags()); out.append(", ");
+
+    if (!hasInfiniteExpirationPeriod()) {
+      out.append("ExpirationPeriod: "); out.append(getExpirationPeriod()); out.append(" milliseconds");
+    }
+    else {
+      out.append("ExpirationPeriod: Infinity");
+    }
+    out.append(")");
+    return out.toString();
+  }
 }
diff --git a/src/main/java/com/intel/jndn/management/types/StatusDataset.java b/src/main/java/com/intel/jndn/management/types/StatusDataset.java
deleted file mode 100644
index 0cc42c8..0000000
--- a/src/main/java/com/intel/jndn/management/types/StatusDataset.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management.types;
-
-import com.intel.jndn.management.ManagementException;
-import java.util.ArrayList;
-import java.util.List;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.encoding.tlv.TlvDecoder;
-import net.named_data.jndn.util.Blob;
-
-/**
- * Helper class to handle StatusDatasets, see
- * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class StatusDataset {
-
-  /**
-   * Decode multiple status entries as part of a StatusDataset, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-   *
-   * @param <T>
-   * @param statusDataset
-   * @param type
-   * @return
-   * @throws com.intel.jndn.management.ManagementException
-   */
-  public static final <T extends Decodable> List<T> wireDecode(Blob statusDataset, Class<T> type) throws ManagementException {
-    List<T> entries = new ArrayList<>();
-    int endOffset = statusDataset.size();
-    TlvDecoder decoder = new TlvDecoder(statusDataset.buf());
-    while (decoder.getOffset() < endOffset) {
-      try {
-        T entry = type.newInstance();
-        entry.wireDecode(decoder);
-        entries.add(entry);
-      } catch (EncodingException | IllegalAccessException | InstantiationException e) {
-        throw new ManagementException("Failed to read status dataset.", e);
-      }
-    }
-    return entries;
-  }
-}
diff --git a/src/main/java/com/intel/jndn/management/types/StrategyChoice.java b/src/main/java/com/intel/jndn/management/types/StrategyChoice.java
index 7718a75..7e092b2 100644
--- a/src/main/java/com/intel/jndn/management/types/StrategyChoice.java
+++ b/src/main/java/com/intel/jndn/management/types/StrategyChoice.java
@@ -13,8 +13,10 @@
  */
 package com.intel.jndn.management.types;
 
-import com.intel.jndn.management.EncodingHelper;
+import com.intel.jndn.management.helpers.EncodingHelper;
 import java.nio.ByteBuffer;
+
+import com.intel.jndn.management.enums.NfdTlv;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.encoding.tlv.TlvDecoder;
@@ -23,17 +25,31 @@
 
 /**
  * Represent a strategy choice entry.
- *
- * @see http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice
+ * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">StrategyChoice</a>
+
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class StrategyChoice implements Decodable {
+  private Name name;
+  private Name strategy;
+
+  /////////////////////////////////////////////////////////////////////////////
 
   /**
-   * TLV type, see
-   * <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#TLV-TYPE-assignments">http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#TLV-TYPE-assignments</a>
+   * Default constructor
    */
-  public final static int TLV_STRATEGY_CHOICE = 128;
+  public StrategyChoice() {
+    // nothing to do
+  }
+
+  /**
+   * Constructor from wire format
+   * @param input wire format
+   * @throws EncodingException
+   */
+  public StrategyChoice(ByteBuffer input) throws EncodingException {
+    wireDecode(input);
+  }
 
   /**
    * Encode using a new TLV encoder.
@@ -55,7 +71,7 @@
     int saveLength = encoder.getLength();
     EncodingHelper.encodeStrategy(strategy, encoder);
     EncodingHelper.encodeName(name, encoder);
-    encoder.writeTypeAndLength(TLV_STRATEGY_CHOICE, encoder.getLength() - saveLength);
+    encoder.writeTypeAndLength(NfdTlv.StrategyChoice, encoder.getLength() - saveLength);
   }
 
   /**
@@ -78,7 +94,7 @@
    */
   @Override
   public final void wireDecode(TlvDecoder decoder) throws EncodingException {
-    int endOffset = decoder.readNestedTlvsStart(TLV_STRATEGY_CHOICE);
+    int endOffset = decoder.readNestedTlvsStart(NfdTlv.StrategyChoice);
     name = EncodingHelper.decodeName(decoder);
     strategy = EncodingHelper.decodeStrategy(decoder);
     decoder.finishNestedTlvs(endOffset);
@@ -101,17 +117,16 @@
   /**
    * @param name the {@link Name} to set
    */
-  public void setName(Name name) {
+  public StrategyChoice setName(Name name) {
     this.name = name;
+    return this;
   }
 
   /**
    * @param strategy the {@link Name} to set
    */
-  public void setStrategy(Name strategy) {
+  public StrategyChoice setStrategy(Name strategy) {
     this.strategy = strategy;
+    return this;
   }
-
-  private Name name;
-  private Name strategy;
 }
diff --git a/src/test/java/com/intel/jndn/management/FaceStatusTest.java b/src/test/java/com/intel/jndn/management/FaceStatusTest.java
deleted file mode 100644
index 742743e..0000000
--- a/src/test/java/com/intel/jndn/management/FaceStatusTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.StatusDataset;
-import com.intel.jndn.management.types.FaceStatus;
-import com.intel.jndn.utils.client.impl.SimpleClient;
-import java.io.IOException;
-import java.util.List;
-import java.util.logging.Logger;
-import junit.framework.Assert;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.Interest;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-/**
- * Test whether the decoding for the face management service is working
- * correctly
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class FaceStatusTest {
-
-  private static final Logger logger = Logger.getLogger(FaceStatusTest.class.getName());
-
-  /**
-   * Test encoding/decoding
-   *
-   * @throws java.lang.Exception
-   */
-  @Test
-  public void testEncodeDecode() throws Exception {
-    FaceStatus status = new FaceStatus();
-    status.setFaceId(42);
-    status.setUri("...");
-    status.setLocalUri("...");
-
-    // encode
-    Blob encoded = status.wireEncode();
-
-    // decode
-    FaceStatus decoded = new FaceStatus();
-    decoded.wireDecode(encoded.buf());
-
-    // test
-    Assert.assertEquals(status.getFaceId(), decoded.getFaceId());
-    Assert.assertEquals(status.getUri(), decoded.getUri());
-    Assert.assertEquals(status.getLocalUri(), decoded.getLocalUri());
-    Assert.assertEquals(status.getExpirationPeriod(), decoded.getExpirationPeriod());
-    Assert.assertEquals(status.getFaceScope(), decoded.getFaceScope());
-    Assert.assertEquals(status.getFacePersistency(), decoded.getFacePersistency());
-    Assert.assertEquals(status.getLinkType(), decoded.getLinkType());
-    Assert.assertEquals(status.getInBytes(), decoded.getInBytes());
-    Assert.assertEquals(status.getOutBytes(), decoded.getOutBytes());
-  }
-
-  /**
-   * Test of decode method, of class FaceStatus.
-   *
-   * @throws java.lang.Exception
-   */
-  @Test
-  public void testDecodeFakeData() throws Exception {
-    Data data = getFaceData(true);
-    List<FaceStatus> results = StatusDataset.wireDecode(data.getContent(), FaceStatus.class);
-    assertTrue(results.size() > 4);
-    for (FaceStatus f : results) {
-      // the first face (face 1) should always be the internal face
-      if (f.getFaceId() == 1) {
-        assertEquals("internal://", f.getUri());
-        assertEquals("internal://", f.getLocalUri());
-      }
-    }
-  }
-
-  /**
-   * Integration test to run on actual system
-   *
-   * @param args
-   * @throws EncodingException
-   */
-  public static void main(String[] args) throws Exception {
-    Data data = getFaceData(false);
-    List<FaceStatus> results = StatusDataset.wireDecode(data.getContent(), FaceStatus.class);
-    assertTrue(results.size() > 4);
-    for (FaceStatus f : results) {
-      // the first face (face 1) should always be the internal face
-      if (f.getFaceId() == 1) {
-        assertEquals("internal://", f.getUri());
-        assertEquals("internal://", f.getLocalUri());
-      }
-    }
-  }
-
-  /**
-   * Retrieve a TLV encoded representation of the face list data
-   *
-   * @param usePreComputedData to avoid errors when local NFD is not present
-   * @return
-   */
-  private static Data getFaceData(boolean usePreComputedData) throws IOException {
-    // use pre-computed data to avoid errors when local NFD is not present
-    if (usePreComputedData) {
-      Data data = new Data();
-      data.setContent(new Blob(hexStringToByteArray(DATA)));
-      return data;
-    } // alternately, query the actual localhost for current data
-    else {
-      Face forwarder = new Face("localhost");
-
-      // build management Interest packet; see <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-      Interest interest = new Interest(new Name("/localhost/nfd/faces/list"));
-      interest.setMustBeFresh(true);
-      interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
-      interest.setInterestLifetimeMilliseconds(2000.0);
-
-      // send packet
-      Data data = SimpleClient.getDefault().getSync(forwarder, interest);
-      String hex = data.getContent().toHex();
-      logger.info("Hex dump of face list: " + hex);
-      return data;
-    }
-  }
-
-  /**
-   * Pre-computed face list from a vanilla NFD running on Ubuntu 14.04
-   */
-  private static final String DATA = "803a690101720b696e7465726e616c3a2f2f810b696e7465726e616c3a2f2f840101850100860100900100910201429202063993010094010095010080406901fe720f636f6e74656e7473746f72653a2f2f810f636f6e74656e7473746f72653a2f2f84010185010086010090010091010092010093010094010095010080306901ff72076e756c6c3a2f2f81076e756c6c3a2f2f8401018501008601009001009101009201009301009401009501008053690201007219756470343a2f2f3232342e302e32332e3137303a35363336338117756470343a2f2f31302e35342e31322e373a35363336338401008501008601009001009101009201009301009401009501008056690201017219756470343a2f2f3232342e302e32332e3137303a3536333633811a756470343a2f2f3139322e3136382e35302e35373a3536333633840100850100860100900100910100920100930100940100950100804869020102721b65746865723a2f2f5b30313a30303a35653a30303a31373a61615d810a6465763a2f2f65746830840100850100860100900100910100920100930100940100950100804969020103721b65746865723a2f2f5b30313a30303a35653a30303a31373a61615d810b6465763a2f2f776c616e30840100850100860100900100910100920100930100940100950100804669020104720766643a2f2f32328114756e69783a2f2f2f72756e2f6e66642e736f636b840101850101860100900206349101019201019302012e940400014079950400041903804e690201197216746370343a2f2f3132372e302e302e313a35363336358115746370343a2f2f3132372e302e302e313a36333633840101850101860100900101910100920100930100940132950100";
-
-  /**
-   * Convert hex string to bytes; special thanks to
-   * <a href="http://stackoverflow.com/questions/140131">http://stackoverflow.com/questions/140131</a>
-   *
-   * @param s
-   * @return
-   */
-  private static byte[] hexStringToByteArray(String s) {
-    int len = s.length();
-    byte[] data = new byte[len / 2];
-    for (int i = 0; i < len; i += 2) {
-      data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
-              + Character.digit(s.charAt(i + 1), 16));
-    }
-    return data;
-  }
-
-}
diff --git a/src/test/java/com/intel/jndn/management/FibEntryTest.java b/src/test/java/com/intel/jndn/management/FibEntryTest.java
deleted file mode 100644
index e966a97..0000000
--- a/src/test/java/com/intel/jndn/management/FibEntryTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.StatusDataset;
-import com.intel.jndn.management.types.NextHopRecord;
-import com.intel.jndn.management.types.FibEntry;
-import com.intel.jndn.utils.client.impl.SimpleClient;
-import java.util.List;
-import junit.framework.Assert;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.Interest;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
-
-/**
- * Test encode/decode of FibEntry and NextHopRecord
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class FibEntryTest {
-
-  /**
-   * Test encoding/decoding
-   *
-   * @throws java.lang.Exception
-   */
-  @Test
-  public void testEncodeDecode() throws Exception {
-    NextHopRecord nextHopRecord = new NextHopRecord();
-    nextHopRecord.setFaceId(42);
-    nextHopRecord.setCost(100);
-    FibEntry entry = new FibEntry();
-    entry.setName(new Name("/fib/entry/test"));
-    entry.getRecords().add(nextHopRecord);
-
-    // encode
-    Blob encoded = entry.wireEncode();
-
-    // decode
-    FibEntry decoded = new FibEntry();
-    decoded.wireDecode(encoded.buf());
-
-    // test
-    Assert.assertEquals(entry.getName().toUri(), decoded.getName().toUri());
-    Assert.assertEquals(entry.getRecords().get(0).getFaceId(), decoded.getRecords().get(0).getFaceId());
-    Assert.assertEquals(entry.getRecords().get(0).getCost(), decoded.getRecords().get(0).getCost());
-  }
-
-  /**
-   * Integration test to run on actual system
-   *
-   * @param args
-   * @throws EncodingException
-   */
-  public static void main(String[] args) throws Exception {
-    Face forwarder = new Face("localhost");
-
-    // build management Interest packet; see <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-    Interest interest = new Interest(new Name("/localhost/nfd/fib/list"));
-    interest.setMustBeFresh(true);
-    interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
-    interest.setInterestLifetimeMilliseconds(2000.0);
-
-    // send packet
-    Data data = SimpleClient.getDefault().getSync(forwarder, interest);
-
-    // decode results
-    List<FibEntry> results = StatusDataset.wireDecode(data.getContent(), FibEntry.class);
-    assertTrue(results.size() > 0);
-    assertEquals("/localhost/nfd", results.get(0).getName().toUri());
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/ForwarderStatusTest.java b/src/test/java/com/intel/jndn/management/ForwarderStatusTest.java
deleted file mode 100644
index bf6b727..0000000
--- a/src/test/java/com/intel/jndn/management/ForwarderStatusTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.ForwarderStatus;
-import junit.framework.Assert;
-import net.named_data.jndn.util.Blob;
-import org.junit.Test;
-
-/**
- * Test encoding/decoding of ForwarderStatus.
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class ForwarderStatusTest {
-
-  /**
-   * Test encoding/decoding
-   *
-   * @throws java.lang.Exception
-   */
-  @Test
-  public void testEncodeDecode() throws Exception {
-    ForwarderStatus status = new ForwarderStatus();
-    status.setNfdVersion("1.0");
-    status.setCurrentTimestamp(System.currentTimeMillis());
-    status.setNumInDatas(42);
-
-    // encode
-    Blob encoded = status.wireEncode();
-
-    // decode
-    ForwarderStatus decoded = new ForwarderStatus();
-    decoded.wireDecode(encoded.buf());
-
-    // test
-    Assert.assertEquals(status.getNfdVersion(), decoded.getNfdVersion());
-    Assert.assertEquals(status.getCurrentTimestamp(), decoded.getCurrentTimestamp());
-    Assert.assertEquals(status.getStartTimestamp(), decoded.getStartTimestamp());
-    Assert.assertEquals(status.getNumInDatas(), decoded.getNumInDatas());
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/IntegrationSuite.java b/src/test/java/com/intel/jndn/management/IntegrationSuite.java
deleted file mode 100644
index 2758e98..0000000
--- a/src/test/java/com/intel/jndn/management/IntegrationSuite.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.RibEntry;
-import java.util.logging.Logger;
-import junit.framework.Assert;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.KeyLocator;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.security.KeyChain;
-
-/**
- * Tests to run with a local NFD as part of integration testing; will not run in
- * the maven test phase, must be run manually.
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class IntegrationSuite {
-
-  private static final Logger logger = Logger.getLogger(IntegrationSuite.class.getName());
-
-  /**
-   * Test NFD methods
-   *
-   * @param args
-   * @throws Exception
-   */
-  public static void main(String[] args) throws Exception {
-    Face face = new Face("localhost");
-    KeyChain keyChain = buildTestKeyChain();
-    face.setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
-
-    Assert.assertTrue(NFD.pingLocal(face));
-    
-    // grab key locator
-    KeyLocator keyLocator = NFD.getKeyLocator(face);
-    Assert.assertNotNull(keyLocator);
-    logger.info("Connected to NFD with key locator: " + keyLocator.getKeyName().toUri());
-
-    // grab datasets
-    Assert.assertTrue(NFD.getForwarderStatus(face).getStartTimestamp() > 0);
-    Assert.assertFalse(NFD.getFaceList(face).isEmpty());
-    Assert.assertFalse(NFD.getFibList(face).isEmpty());
-    Assert.assertFalse(NFD.getRouteList(face).isEmpty());
-
-    // create a new route
-    NFD.register(face, "udp4://127.0.0.1:56363", new Name("/my/test/route"), 999);
-
-    // check that route is created
-    Thread.sleep(1000); // NFD registers the route asynchronously
-    boolean found = false;
-    for (RibEntry route : NFD.getRouteList(face)) {
-      logger.info("Found route: " + route.getName().toUri());
-      if (route.getName().equals(new Name("/my/test/route"))) {
-        found = true;
-      }
-    }
-    Assert.assertTrue(found);
-
-    // remove the route
-    NFD.unregister(face, new Name("/my/test/route"), "udp4://127.0.0.1:56363");
-  }
-
-  /**
-   * Setup the KeyChain with a default identity; TODO move this to
-   * MemoryIdentityStorage once it can handle getting/setting defaults
-   *
-   * @return
-   * @throws net.named_data.jndn.security.SecurityException
-   */
-  public static KeyChain buildTestKeyChain() throws net.named_data.jndn.security.SecurityException {
-    KeyChain keyChain = new KeyChain();
-    try {
-      keyChain.getDefaultCertificateName();
-    } catch (net.named_data.jndn.security.SecurityException e) {
-      keyChain.createIdentity(new Name("/test/identity"));
-      keyChain.getIdentityManager().setDefaultIdentity(new Name("/test/identity"));
-    }
-    return keyChain;
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/LocalControlHeaderTest.java b/src/test/java/com/intel/jndn/management/LocalControlHeaderTest.java
deleted file mode 100644
index 709c8a1..0000000
--- a/src/test/java/com/intel/jndn/management/LocalControlHeaderTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.LocalControlHeader;
-import com.intel.jndn.utils.client.impl.SimpleClient;
-import java.util.logging.Logger;
-import junit.framework.Assert;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.security.KeyChain;
-
-/**
- * Test functionality for LocalControlHeader
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class LocalControlHeaderTest {
-
-  private static final Logger logger = Logger.getLogger(IntegrationSuite.class.getName());
-
-  /**
-   * Integration test to run on actual system
-   *
-   * @param args
-   * @throws EncodingException
-   */
-  public static void main(String[] args) throws Exception {
-    // setup forwarder face
-    Face forwarder = new Face("localhost");
-    KeyChain keyChain = IntegrationSuite.buildTestKeyChain();
-    forwarder.setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
-
-    // enable incoming face ID header
-    NFD.enableLocalControlHeader(forwarder, LocalControlHeader.INCOMING_FACE_ID);
-
-    // use and verify
-    Data data = SimpleClient.getDefault().getSync(forwarder, new Name("/localhost/nfd"));
-    long faceId = data.getIncomingFaceId();
-    Assert.assertTrue(faceId != -1); // this verifies that the headers are working correctly
-    logger.info("Face ID for this client on the forwarder: " + faceId);
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/NdnPingClientIT.java b/src/test/java/com/intel/jndn/management/NdnPingClientIT.java
new file mode 100644
index 0000000..b561b6e
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/NdnPingClientIT.java
@@ -0,0 +1,55 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.management.enums.Strategies;
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.StrategyChoice;
+import com.intel.jndn.mock.MockKeyChain;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.KeyLocator;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Logger;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.*;
+
+/**
+ * Testing basic pining using real NFD instance (NFD must be run locally while executing the test)
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NdnPingClientIT {
+  private Face face;
+
+  @Before
+  public void setUp() throws SecurityException {
+    face = new Face("localhost");
+  }
+
+  @Test
+  public void testPingLocal() throws IOException, ManagementException {
+    boolean hasSucceeded = NdnPingClient.pingLocal(face);
+    assertTrue(hasSucceeded);
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/NfdcIT.java b/src/test/java/com/intel/jndn/management/NfdcIT.java
new file mode 100644
index 0000000..8a8a0c3
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/NfdcIT.java
@@ -0,0 +1,126 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Logger;
+
+import com.intel.jndn.management.enums.LocalControlHeader;
+import com.intel.jndn.management.enums.Strategies;
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.StrategyChoice;
+import com.intel.jndn.mock.MockKeyChain;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.KeyLocator;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.*;
+
+/**
+ * Testing Nfdc with real NFD instance (NFD must be run locally while executing the test)
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NfdcIT {
+  private static final Logger LOG = Logger.getLogger(NfdcIT.class.getName());
+  private Face face;
+
+  @Before
+  public void setUp() throws SecurityException {
+    face = new Face("localhost");
+    KeyChain keyChain = MockKeyChain.configure(new Name("/tmp/identity"));
+    face.setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
+  }
+
+  @Test
+  public void testConnectivity() throws IOException, ManagementException {
+    KeyLocator keyLocator = Nfdc.getKeyLocator(face);
+    assertNotNull(keyLocator);
+    LOG.info("Connected to NFD with key locator: " + keyLocator.getKeyName().toUri());
+  }
+
+  @Test
+  public void testStatusDatasets() throws Exception {
+    assertTrue(Nfdc.getForwarderStatus(face).getStartTimestamp() > 0);
+    assertFalse(Nfdc.getFaceList(face).isEmpty());
+    assertFalse(Nfdc.getFibList(face).isEmpty());
+    assertFalse(Nfdc.getRouteList(face).isEmpty());
+  }
+
+  @Test
+  public void testRoutes() throws EncodingException, IOException, ManagementException, InterruptedException {
+    Nfdc.register(face, new Name("/my/route/to/app/face"), 333);
+    int faceId = Nfdc.createFace(face, "udp4://127.0.0.1:56363");
+    Nfdc.register(face, "udp4://127.0.0.1:56363", new Name("/my/test/route"), 999);
+    Nfdc.register(face, faceId, new Name("/"), 555);
+
+    // check that route is created
+    Thread.sleep(1000); // NFD registers the route asynchronously
+
+    boolean found = false;
+    for (RibEntry route : Nfdc.getRouteList(face)) {
+      LOG.info("Found route: " + route.getName().toUri());
+      if (route.getName().equals(new Name("/my/test/route"))) {
+        found = true;
+      }
+    }
+    assertTrue(found);
+
+    Nfdc.unregister(face, new Name("/my/route/to/app/face"));
+
+    // remove the route
+    Nfdc.unregister(face, new Name("/my/test/route"), "udp4://127.0.0.1:56363");
+
+    // remove face
+    Nfdc.destroyFace(face, faceId);
+  }
+
+  @Test
+  public void testStrategies() throws Exception {
+    Name prefix = new Name("/test/strategy").append("random:" + new Random().nextInt());
+
+    List<StrategyChoice> choices = Nfdc.getStrategyList(face);
+    int oldSize = choices.size();
+
+    Nfdc.setStrategy(face, prefix, Strategies.CLIENT_CONTROL);
+    Thread.sleep(1000); // strategy takes a while to register
+
+    choices = Nfdc.getStrategyList(face);
+    assertEquals(oldSize + 1, choices.size());
+
+    Nfdc.unsetStrategy(face, prefix);
+  }
+
+  /**
+   * LocalControlHeader would work only with NFD < 0.3.4, broken otherwise
+   */
+  @Test(expected = ManagementException.class)
+  public void testLocalControlHeader() throws Exception {
+    Nfdc.enableLocalControlHeader(face, LocalControlHeader.INCOMING_FACE_ID);
+    Thread.sleep(1000); // strategy takes a while to register
+
+    // TODO: add asserts
+
+    Nfdc.disableLocalControlHeader(face, LocalControlHeader.INCOMING_FACE_ID);
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/RibEntryTest.java b/src/test/java/com/intel/jndn/management/RibEntryTest.java
deleted file mode 100644
index 9494239..0000000
--- a/src/test/java/com/intel/jndn/management/RibEntryTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.StatusDataset;
-import com.intel.jndn.management.types.RibEntry;
-import com.intel.jndn.management.types.Route;
-import com.intel.jndn.utils.client.impl.SimpleClient;
-import java.util.List;
-import junit.framework.Assert;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.Interest;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
-
-/**
- * Test encoding/decoding for RibEntry.
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class RibEntryTest {
-
-  /**
-   * Test encoding/decoding
-   *
-   * @throws java.lang.Exception
-   */
-  @Test
-  public void testEncodeDecode() throws Exception {
-    Route route = new Route();
-    route.setFaceId(42);
-    route.setCost(100);
-    route.setOrigin(0);
-    RibEntry entry = new RibEntry();
-    entry.setName(new Name("/rib/entry/test"));
-    entry.getRoutes().add(route);
-
-    // encode
-    Blob encoded = entry.wireEncode();
-
-    // decode
-    RibEntry decoded = new RibEntry();
-    decoded.wireDecode(encoded.buf());
-
-    // test
-    Assert.assertEquals(entry.getName().toUri(), decoded.getName().toUri());
-    Assert.assertEquals(entry.getRoutes().get(0).getFaceId(), decoded.getRoutes().get(0).getFaceId());
-    Assert.assertEquals(entry.getRoutes().get(0).getCost(), decoded.getRoutes().get(0).getCost());
-    Assert.assertEquals(entry.getRoutes().get(0).getOrigin(), decoded.getRoutes().get(0).getOrigin());
-  }
-
-  /**
-   * Integration test to run on actual system
-   *
-   * @param args
-   * @throws EncodingException
-   */
-  public static void main(String[] args) throws Exception {
-    Face forwarder = new Face("localhost");
-
-    // build management Interest packet; see <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
-    Interest interest = new Interest(new Name("/localhost/nfd/rib/list"));
-    interest.setMustBeFresh(true);
-    interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
-    interest.setInterestLifetimeMilliseconds(2000.0);
-
-    // send packet
-    Data data = SimpleClient.getDefault().getSync(forwarder, interest);
-
-    // decode results
-    List<RibEntry> results = StatusDataset.wireDecode(data.getContent(), RibEntry.class);
-    assertTrue(results.size() > 0);
-    assertEquals("/localhost/nfd/rib", results.get(0).getName().toUri());
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/StrategyTestIT.java b/src/test/java/com/intel/jndn/management/StrategyTestIT.java
deleted file mode 100644
index c0abd14..0000000
--- a/src/test/java/com/intel/jndn/management/StrategyTestIT.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * jndn-management
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.management;
-
-import com.intel.jndn.management.types.StrategyChoice;
-import com.intel.jndn.mock.MockKeyChain;
-import java.util.List;
-import java.util.Random;
-import java.util.logging.Logger;
-import static junit.framework.Assert.assertEquals;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.security.KeyChain;
-import org.junit.Test;
-
-/**
- * Test strategy management on a real, local NFD
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class StrategyTestIT {
-
-  private static final Logger logger = Logger.getLogger(StrategyTestIT.class.getName());
-  Name prefix;
-  Face face;
-
-  public StrategyTestIT() throws net.named_data.jndn.security.SecurityException {
-    this.prefix = new Name("/test/strategy").append("random:" + new Random().nextInt());
-    this.face = new Face("localhost"); // strategy commands only available on localhost
-    KeyChain mockKeyChain = MockKeyChain.configure(new Name("/test/server"));
-    face.setCommandSigningInfo(mockKeyChain, mockKeyChain.getDefaultCertificateName());
-  }
-
-  @Test
-  public void testStrategySetUnset() throws Exception {
-    List<StrategyChoice> choices = NFD.getStrategyList(face);
-    int oldSize = choices.size();
-
-    NFD.setStrategy(face, prefix, Strategies.CLIENT_CONTROL);
-    Thread.sleep(1000); // strategy takes a while to register
-
-    choices = NFD.getStrategyList(face);
-    assertEquals(oldSize + 1, choices.size());
-
-    NFD.unsetStrategy(face, prefix);
-  }
-}
diff --git a/src/test/java/com/intel/jndn/management/TestHelper.java b/src/test/java/com/intel/jndn/management/TestHelper.java
new file mode 100644
index 0000000..09ea326
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/TestHelper.java
@@ -0,0 +1,42 @@
+/*
+ * jndn-management
+ * Copyright (c) 2016, Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Helper methods for unit tests
+ */
+public class TestHelper {
+  /**
+   * Prevent instances of TestHelper
+   */
+  private TestHelper() {
+  }
+
+  /**
+   * Construct ByteBuffer from int[]
+   */
+  public static ByteBuffer
+  bufferFromIntArray(int[] array)
+  {
+    ByteBuffer result = ByteBuffer.allocate(array.length);
+    for (int value : array) {
+      result.put((byte) (value & 0xFF));
+    }
+
+    result.flip();
+    return result;
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java b/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java
new file mode 100644
index 0000000..b6b959b
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java
@@ -0,0 +1,103 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.TestHelper;
+import com.intel.jndn.management.enums.FacePersistency;
+import com.intel.jndn.management.enums.FaceScope;
+import com.intel.jndn.management.enums.LinkType;
+
+import java.nio.ByteBuffer;
+import net.named_data.jndn.util.Blob;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test whether the decoding for the face management service is working
+ * correctly
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class FaceStatusTest {
+  ByteBuffer TestFaceStatus;
+
+  @Before
+  public void setUp() throws Exception {
+    TestFaceStatus = TestHelper.bufferFromIntArray(new int[] {
+      0x80, 0x5e, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70,
+      0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e,
+      0x32, 0x2e, 0x31, 0x3a, 0x36, 0x33, 0x36, 0x33, 0x81, 0x16,
+      0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+      0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32, 0x3a, 0x35, 0x35, 0x35,
+      0x35, 0x35, 0x6d, 0x02, 0x27, 0x10, 0x84, 0x01, 0x01, 0x85,
+      0x01, 0x01, 0x86, 0x01, 0x01, 0x90, 0x01, 0x0a, 0x91, 0x01,
+      0xc8, 0x97, 0x01, 0x01, 0x92, 0x02, 0x0b, 0xb8, 0x93, 0x01,
+      0x04, 0x98, 0x01, 0x02, 0x94, 0x04, 0x4f, 0x41, 0xe7, 0x7b,
+      0x95, 0x04, 0x3b, 0x8d, 0x37, 0x30,
+    });
+  }
+
+  /**
+   * Test encoding
+   */
+  @Test
+  public void testEncode() throws Exception {
+    FaceStatus status = new FaceStatus();
+    status.setFaceId(100)
+      .setRemoteUri("tcp4://192.0.2.1:6363")
+      .setLocalUri("tcp4://192.0.2.2:55555")
+      .setFaceScope(FaceScope.LOCAL)
+      .setFacePersistency(FacePersistency.ON_DEMAND)
+      .setLinkType(LinkType.MULTI_ACCESS)
+      .setExpirationPeriod(10000)
+      .setNInInterests(10)
+      .setNInDatas(200)
+      .setNInNacks(1)
+      .setNOutInterests(3000)
+      .setNOutDatas(4)
+      .setNOutNacks(2)
+      .setNInBytes(1329719163)
+      .setNOutBytes(999110448);
+
+    // encode
+    Blob encoded = status.wireEncode();
+    assertEquals(TestFaceStatus, encoded.buf());
+  }
+
+  /**
+   * Test decoding
+   */
+  @Test
+  public void testDecode() throws Exception {
+    FaceStatus status = new FaceStatus(TestFaceStatus);
+
+    assertEquals(100, status.getFaceId());
+    assertEquals("tcp4://192.0.2.1:6363", status.getRemoteUri());
+    assertEquals("tcp4://192.0.2.2:55555", status.getLocalUri());
+    assertEquals(FaceScope.LOCAL, status.getFaceScope());
+    assertEquals(FacePersistency.ON_DEMAND, status.getFacePersistency());
+    assertEquals(LinkType.MULTI_ACCESS, status.getLinkType());
+    assertEquals(10000, status.getExpirationPeriod());
+    assertEquals(10, status.getNInInterests());
+    assertEquals(200, status.getNInDatas());
+    assertEquals(1, status.getNInNacks());
+    assertEquals(3000, status.getNOutInterests());
+    assertEquals(4, status.getNOutDatas());
+    assertEquals(2, status.getNOutNacks());
+    assertEquals(1329719163, status.getNInBytes());
+    assertEquals(999110448, status.getNOutBytes());
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/types/FibEntryTest.java b/src/test/java/com/intel/jndn/management/types/FibEntryTest.java
new file mode 100644
index 0000000..d8c19f4
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/types/FibEntryTest.java
@@ -0,0 +1,138 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.TestHelper;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test encode/decode of FibEntry and NextHopRecord
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class FibEntryTest {
+  ByteBuffer TestNextHopRecord;
+  ByteBuffer TestFibEntryNoNextHops;
+  ByteBuffer TestFibEntry;
+
+  @Before
+  public void setUp() {
+    TestNextHopRecord = TestHelper.bufferFromIntArray(new int[] {
+      0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8
+    });
+
+    TestFibEntryNoNextHops = TestHelper.bufferFromIntArray(new int[] {
+      0x80, 0x15, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73,
+      0x08, 0x02, 0x69, 0x73, 0x08, 0x01, 0x61, 0x08, 0x04, 0x74,
+      0x65, 0x73, 0x74
+    });
+    TestFibEntry = TestHelper.bufferFromIntArray(new int[] {
+      0x80, 0x38, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73, 0x08, 0x02, 0x69, 0x73, 0x08, 0x01,
+      0x61, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8, 0x81,
+      0x07, 0x69, 0x01, 0x14, 0x6a, 0x02, 0x01, 0x2c, 0x81, 0x07, 0x69, 0x01, 0x1e, 0x6a, 0x02, 0x01,
+      0x90, 0x81, 0x07, 0x69, 0x01, 0x28, 0x6a, 0x02, 0x01, 0xf4
+    });
+  }
+
+  @Test
+  public void testNextHopRecordEncode()
+  {
+    NextHopRecord record = new NextHopRecord();
+    record.setFaceId(10);
+    record.setCost(200);
+
+    ByteBuffer wire = record.wireEncode().buf();
+    assertEquals(TestNextHopRecord, wire);
+  }
+
+  @Test
+  public void testNextHopRecordDecode() throws EncodingException {
+    NextHopRecord record = new NextHopRecord(TestNextHopRecord);
+
+    assertEquals(10, record.getFaceId());
+    assertEquals(200, record.getCost());
+  }
+
+  @Test
+  public void testFibEntryNoNextHopEncode()
+  {
+    FibEntry entry = new FibEntry();
+    entry.setPrefix(new Name("/this/is/a/test"));
+
+    ByteBuffer wire = entry.wireEncode().buf();
+    assertEquals(TestFibEntryNoNextHops, wire);
+  }
+
+  @Test
+  public void testFibEntryNoNextHopsDecode() throws EncodingException {
+    FibEntry entry = new FibEntry(TestFibEntryNoNextHops);
+
+    assertEquals("/this/is/a/test", entry.getPrefix().toString());
+    assertEquals(0, entry.getNextHopRecords().size());
+  }
+
+  @Test
+  public void testFibEntryEncode()
+  {
+    FibEntry entry = new FibEntry();
+    entry.setPrefix(new Name("/this/is/a/test"));
+
+    List<NextHopRecord> records = new ArrayList<>();
+    for (int i = 1; i < 4; i++)
+    {
+      NextHopRecord record = new NextHopRecord();
+      record.setFaceId(i * 10);
+      record.setCost((i * 100) + 100);
+      records.add(record);
+    }
+
+    entry.setNextHopRecords(records);
+
+    NextHopRecord oneMore = new NextHopRecord();
+    oneMore.setFaceId(40);
+    oneMore.setCost(500);
+
+    entry.addNextHopRecord(oneMore);
+
+    ByteBuffer wire = entry.wireEncode().buf();
+    assertEquals(TestFibEntry, wire);
+  }
+
+  @Test
+  public void testFibEntryDecode() throws EncodingException {
+    FibEntry entry = new FibEntry(TestFibEntry);
+
+    List<NextHopRecord> records = entry.getNextHopRecords();
+
+    assertEquals("/this/is/a/test", entry.getPrefix().toUri());
+    assertEquals(4, entry.getNextHopRecords().size());
+
+    int value = 1;
+
+    for (NextHopRecord record : records) {
+      assertEquals(value * 10, record.getFaceId());
+      assertEquals((value * 100) + 100, record.getCost());
+      ++value;
+    }
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/types/ForwarderStatusTest.java b/src/test/java/com/intel/jndn/management/types/ForwarderStatusTest.java
new file mode 100644
index 0000000..5ff5157
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/types/ForwarderStatusTest.java
@@ -0,0 +1,88 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import java.nio.ByteBuffer;
+
+import com.intel.jndn.management.TestHelper;
+import net.named_data.jndn.encoding.EncodingException;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test encoding/decoding of ForwarderStatus.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class ForwarderStatusTest {
+  ByteBuffer TestForwarderStatus;
+
+  @Before
+  public void setUp() throws Exception {
+    TestForwarderStatus = TestHelper.bufferFromIntArray(new int[] {
+      0x80, 0x11, 0x30, 0x2e, 0x32, 0x2e, 0x30, 0x2d, 0x36, 0x35,
+      0x2d, 0x67, 0x37, 0x35, 0x61, 0x62, 0x36, 0x62, 0x37, 0x81, 0x08, 0x00,
+      0x00, 0x00, 0x57, 0x5b, 0x42, 0xa6, 0x2d, 0x82, 0x08, 0x00, 0x00, 0x00,
+      0xce, 0x50, 0x36, 0xd7, 0x20, 0x83, 0x04, 0x6e, 0x43, 0xe4, 0x78, 0x84,
+      0x04, 0x25, 0x0e, 0xfe, 0xe4, 0x85, 0x04, 0x1c, 0xbc, 0xb7, 0x4d, 0x86,
+      0x04, 0x69, 0x9a, 0x61, 0xf2, 0x87, 0x04, 0x4b, 0x65, 0xe3, 0xf0, 0x90,
+      0x04, 0x24, 0x86, 0xc3, 0x5f, 0x91, 0x04, 0x6d, 0xe2, 0xbc, 0xf2, 0x97,
+      0x02, 0x04, 0xd2, 0x92, 0x04, 0x38, 0xc0, 0x92, 0x3d, 0x93, 0x04, 0x08,
+      0x3c, 0xbf, 0x2a, 0x98, 0x02, 0x10, 0xe1,
+    });
+  }
+
+  @Test
+  public void testEncode() {
+    ForwarderStatus status = new ForwarderStatus();
+    status.setNfdVersion("0.2.0-65-g75ab6b7");
+    status.setStartTimestamp(375193249325L);
+    status.setCurrentTimestamp(886109034272L);
+    status.setNNameTreeEntries(1849943160);
+    status.setNFibEntries(621739748);
+    status.setNPitEntries(482129741);
+    status.setNMeasurementsEntries(1771725298);
+    status.setNCsEntries(1264968688);
+    status.setNInInterests(612811615);
+    status.setNInDatas(1843576050);
+    status.setNInNacks(1234);
+    status.setNOutInterests(952144445);
+    status.setNOutDatas(138198826);
+    status.setNOutNacks(4321);
+
+    ByteBuffer wire = status.wireEncode().buf();
+    assertEquals(TestForwarderStatus, wire);
+  }
+
+  @Test
+  public void testDecode() throws EncodingException {
+    ForwarderStatus status = new ForwarderStatus(TestForwarderStatus);
+
+    assertEquals("0.2.0-65-g75ab6b7", status.getNfdVersion());
+    assertEquals(375193249325L,       status.getStartTimestamp());
+    assertEquals(886109034272L,       status.getCurrentTimestamp());
+    assertEquals(1849943160,          status.getNNameTreeEntries());
+    assertEquals(621739748,           status.getNFibEntries());
+    assertEquals(482129741,           status.getNPitEntries());
+    assertEquals(1771725298,          status.getNMeasurementsEntries());
+    assertEquals(1264968688,          status.getNCsEntries());
+    assertEquals(612811615,           status.getNInInterests());
+    assertEquals(1843576050,          status.getNInDatas());
+    assertEquals(1234,                status.getNInNacks());
+    assertEquals(952144445,           status.getNOutInterests());
+    assertEquals(138198826,           status.getNOutDatas());
+    assertEquals(4321,                status.getNOutNacks());
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/types/RibEntryTest.java b/src/test/java/com/intel/jndn/management/types/RibEntryTest.java
new file mode 100644
index 0000000..82b4d22
--- /dev/null
+++ b/src/test/java/com/intel/jndn/management/types/RibEntryTest.java
@@ -0,0 +1,288 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import java.nio.ByteBuffer;
+
+import com.intel.jndn.management.TestHelper;
+import com.intel.jndn.management.enums.RouteFlags;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import net.named_data.jndn.Name;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RibEntryTest {
+  ByteBuffer RouteData;
+  ByteBuffer RouteInfiniteExpirationPeriod;
+  ByteBuffer RibEntryData;
+  ByteBuffer RibEntryInfiniteExpirationPeriod;
+  ByteBuffer RibEntryForRootData;
+
+  @Before
+  public void setUp() throws Exception {
+    RouteData = TestHelper.bufferFromIntArray(new int[] {
+        0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+        0x6d, 0x02, 0x27, 0x10
+      });
+    RouteInfiniteExpirationPeriod = TestHelper.bufferFromIntArray(new int[] {
+        0x81, 0x0C, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02
+      });
+    RibEntryData = TestHelper.bufferFromIntArray(new int[] {
+        // Header + Name (ndn:/hello/world)
+        0x80, 0x34, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+        0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64,
+        // Route
+        0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+        0x6d, 0x02, 0x27, 0x10,
+        // Route
+        0x81, 0x10, 0x69, 0x01, 0x02, 0x6f, 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01,
+        0x6d, 0x02, 0x13, 0x88
+      });
+    RibEntryInfiniteExpirationPeriod = TestHelper.bufferFromIntArray(new int[] {
+        // Header + Name (ndn:/hello/world)
+        0x80, 0x30, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+        0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64,
+        // Route
+        0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+        0x6d, 0x02, 0x27, 0x10,
+        // Route with no ExpirationPeriod
+        0x81, 0x0C, 0x69, 0x01, 0x02, 0x6f, 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01,
+      });
+    RibEntryForRootData = TestHelper.bufferFromIntArray(new int[] {
+        // Header + Name (ndn:/)
+        0x80, 0x26, 0x07, 0x00,
+        // Route
+        0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+        0x6d, 0x02, 0x27, 0x10,
+        // Route
+        0x81, 0x10, 0x69, 0x01, 0x02, 0x6f, 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01,
+        0x6d, 0x02, 0x13, 0x88
+      });
+  }
+
+  @Test
+  public void testRouteEncode() throws Exception {
+    Route route = new Route();
+    route.setFaceId(1);
+    route.setOrigin(128);
+    route.setCost(100);
+    route.setFlags(RouteFlags.CAPTURE.toInteger());
+    route.setExpirationPeriod(10000);
+
+    assertEquals(RouteData, route.wireEncode().buf());
+  }
+
+  @Test
+  public void testRouteDecode() throws Exception {
+    Route route = new Route(RouteData);
+
+    assertEquals(route.getFaceId(), 1);
+    assertEquals(route.getOrigin(), 128);
+    assertEquals(route.getCost(), 100);
+    assertEquals(RouteFlags.CAPTURE.toInteger(), route.getFlags());
+    assertEquals(route.getExpirationPeriod(), 10000);
+    assertEquals(route.hasInfiniteExpirationPeriod(), false);
+  }
+
+  @Test
+  public void testRouteInfiniteExpirationPeriodEncode() throws Exception {
+    Route route = new Route();
+    route.setFaceId(1);
+    route.setOrigin(128);
+    route.setCost(100);
+    route.setFlags(RouteFlags.CAPTURE.toInteger());
+    route.setExpirationPeriod(Route.INFINITE_EXPIRATION_PERIOD);
+
+    assertEquals(RouteInfiniteExpirationPeriod, route.wireEncode().buf());
+  }
+
+  @Test
+  public void testRouteInfiniteExpirationPeriodDecode() throws Exception {
+    Route route = new Route(RouteInfiniteExpirationPeriod);
+
+    assertEquals(route.getFaceId(), 1);
+    assertEquals(route.getOrigin(), 128);
+    assertEquals(route.getCost(), 100);
+    assertEquals(RouteFlags.CAPTURE.toInteger(), route.getFlags());
+    assertEquals(route.getExpirationPeriod(), Route.INFINITE_EXPIRATION_PERIOD);
+    assertEquals(route.hasInfiniteExpirationPeriod(), true);
+  }
+
+  @Test
+  public void testRouteOutputStream() throws Exception {
+    Route route = new Route();
+    route.setFaceId(1);
+    route.setOrigin(128);
+    route.setCost(100);
+    route.setFlags(RouteFlags.CAPTURE.toInteger());
+    route.setExpirationPeriod(10000);
+
+    assertEquals(route.toString(), "Route(FaceId: 1, Origin: 128, Cost: 100, " +
+                                   "Flags: 2, ExpirationPeriod: 10000 milliseconds)");
+  }
+
+  @Test
+  public void testRibEntryEncode() throws Exception {
+    RibEntry entry = newRibEntry("/hello/world", 2, false);
+
+    assertEquals(RibEntryData, entry.wireEncode().buf());
+  }
+
+  @Test
+  public void testRibEntryDecode() throws Exception {
+    RibEntry entry = new RibEntry(RibEntryData);
+
+    assertRibEntry(entry, "/hello/world", 2, false);
+  }
+
+  @Test
+  public void testRibEntryForRootEncode() throws Exception {
+    RibEntry entry = newRibEntry("/", 2, false);
+
+    assertEquals(RibEntryForRootData, entry.wireEncode().buf());
+  }
+
+  @Test
+  public void testRibEntryForRootDecode() throws Exception {
+    RibEntry entry = new RibEntry(RibEntryForRootData);
+
+    assertRibEntry(entry, "/", 2, false);
+  }
+
+  @Test
+  public void testRibEntryInfiniteExpirationPeriodEncode() throws Exception {
+    RibEntry entry = newRibEntry("/hello/world", 2, true);
+    assertEquals(RibEntryInfiniteExpirationPeriod, entry.wireEncode().buf());
+  }
+
+  @Test
+  public void testRibEntryInfiniteExpirationPeriodDecode() throws Exception {
+    RibEntry entry = new RibEntry();
+
+    entry.wireDecode(RibEntryInfiniteExpirationPeriod);
+    assertRibEntry(entry, "/hello/world", 2, true);
+  }
+
+  @Test
+  public void testRibEntryClear() throws Exception {
+    RibEntry entry = newRibEntry("/hello/world", 2, true);
+    assertEquals(entry.getRoutes().size(), 2);
+    assertRibEntry(entry, "/hello/world", 2, true);
+
+    entry.clearRoutes();
+    assertEquals(entry.getRoutes().size(), 0);
+  }
+
+  @Test
+  public void testRibEntryOutputStream() throws Exception {
+    RibEntry entry = newRibEntry("/hello/world", 2, true);
+
+    assertEquals("RibEntry{\n" +
+                 "  Name: /hello/world\n" +
+                 "  Route(FaceId: 1, Origin: 128, Cost: 100, Flags: 2, ExpirationPeriod: 10000 milliseconds)\n" +
+                 "  Route(FaceId: 2, Origin: 0, Cost: 32, Flags: 1, ExpirationPeriod: Infinity)\n" +
+                 "}",
+                 entry.toString());
+  }
+
+  @Test
+  public void testRibEntryAddRoutes() {
+    RibEntry a = newRibEntry("/hello/world", 2, true);
+
+    RibEntry b = new RibEntry();
+    b.setName(new Name("/another/prefix"));
+    b.setRoutes(a.getRoutes());
+
+    assertEquals("RibEntry{\n" +
+                 "  Name: /another/prefix\n" +
+                 "  Route(FaceId: 1, Origin: 128, Cost: 100, Flags: 2, ExpirationPeriod: 10000 milliseconds)\n" +
+                 "  Route(FaceId: 2, Origin: 0, Cost: 32, Flags: 1, ExpirationPeriod: Infinity)\n" +
+                 "}",
+                 b.toString());
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+
+  private RibEntry newRibEntry(String name, int nRoutes, boolean isInfiniteSecond) {
+    RibEntry entry = new RibEntry();
+    entry.setName(new Name(name));
+
+    Route route1 = new Route();
+    route1.setFaceId(1);
+    route1.setOrigin(128);
+    route1.setCost(100);
+    route1.setFlags(RouteFlags.CAPTURE.toInteger());
+    route1.setExpirationPeriod(10000);
+    entry.addRoute(route1);
+
+    if (nRoutes > 1) {
+      Route route2 = new Route();
+      route2.setFaceId(2);
+      route2.setOrigin(0);
+      route2.setCost(32);
+      route2.setFlags(RouteFlags.CHILD_INHERIT.toInteger());
+      if (isInfiniteSecond) {
+        route2.setExpirationPeriod(Route.INFINITE_EXPIRATION_PERIOD);
+      }
+      else {
+        route2.setExpirationPeriod(5000);
+      }
+      entry.addRoute(route2);
+    }
+
+    return entry;
+  }
+
+  private void assertRibEntry(RibEntry entry, String name, int nRoutes, boolean isInfiniteSecond) throws Exception {
+    assertEquals(entry.getName().toUri(), name);
+    assertEquals(entry.getRoutes().size(), nRoutes);
+
+    List<Route> routes = entry.getRoutes();
+
+    ListIterator<Route> it = routes.listIterator();
+    assertTrue(it.hasNext());
+    Route item = it.next();
+    assertEquals(item.getFaceId(), 1);
+    assertEquals(item.getOrigin(), 128);
+    assertEquals(item.getCost(), 100);
+    assertEquals(RouteFlags.CAPTURE.toInteger(), item.getFlags());
+    assertEquals(item.getExpirationPeriod(), 10000);
+    assertEquals(item.hasInfiniteExpirationPeriod(), false);
+
+    if (nRoutes > 1) {
+      assertTrue(it.hasNext());
+      item = it.next();
+      assertEquals(item.getFaceId(), 2);
+      assertEquals(item.getOrigin(), 0);
+      assertEquals(item.getCost(), 32);
+      assertEquals(RouteFlags.CHILD_INHERIT.toInteger(), item.getFlags());
+
+      if (isInfiniteSecond) {
+        assertEquals(item.getExpirationPeriod(), Route.INFINITE_EXPIRATION_PERIOD);
+        assertEquals(item.hasInfiniteExpirationPeriod(), true);
+      }
+      else {
+        assertEquals(item.getExpirationPeriod(), 5000);
+        assertEquals(item.hasInfiniteExpirationPeriod(), false);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/intel/jndn/management/types/StrategyChoiceTest.java b/src/test/java/com/intel/jndn/management/types/StrategyChoiceTest.java
index 1b2ab64..077d91c 100644
--- a/src/test/java/com/intel/jndn/management/types/StrategyChoiceTest.java
+++ b/src/test/java/com/intel/jndn/management/types/StrategyChoiceTest.java
@@ -11,41 +11,53 @@
  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  * more details.
  */
-package com.intel.jndn.management.types;
+package com.intel.jndn.management;
 
-import com.intel.jndn.management.Strategies;
-import junit.framework.Assert;
+import com.intel.jndn.management.types.StrategyChoice;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
+import org.junit.Before;
 import org.junit.Test;
 
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertEquals;
+
 /**
- * Test StrategyCHoice encoding/decoding
+ * Test StrategyChoice encoding/decoding
  *
  * @author Andrew Brown <andrew.brown@intel.com>
  */
 public class StrategyChoiceTest {
+  ByteBuffer TestStrategyChoice;
 
-  /**
-   * Test of wireEncode method, of class StrategyChoice.
-   * @throws net.named_data.jndn.encoding.EncodingException
-   */
+  @Before
+  public void setUp() throws Exception {
+    TestStrategyChoice = TestHelper.bufferFromIntArray(new int[] {
+      0x80, 0x39, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x08, 0x05, 0x77,
+      0x6f, 0x72, 0x6c, 0x64, 0x6b, 0x27, 0x07, 0x25, 0x08, 0x04, 0x73, 0x6f, 0x6d, 0x65,
+      0x08, 0x03, 0x6e, 0x6f, 0x6e, 0x08, 0x08, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e,
+      0x67, 0x08, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x08, 0x04, 0x6e,
+      0x61, 0x6d, 0x65
+    });
+  }
+
   @Test
-  public void testEncodeDecode() throws EncodingException {
-    StrategyChoice choice = new StrategyChoice();
-    choice.setName(new Name("/a/b"));
-    choice.setStrategy(Strategies.NCC);
+  public void testEncode() {
+    StrategyChoice strategyChoice = new StrategyChoice();
+    strategyChoice
+      .setName(new Name("/hello/world"))
+      .setStrategy(new Name("/some/non/existing/strategy/name"))
+    ;
 
-    // encode
-    Blob encoded = choice.wireEncode();
+    ByteBuffer wire = strategyChoice.wireEncode().buf();
+    assertEquals(TestStrategyChoice, wire);
+  }
 
-    // decode
-    StrategyChoice decoded = new StrategyChoice();
-    decoded.wireDecode(encoded.buf());
-
-    // test
-    Assert.assertEquals(choice.getName().toUri(), decoded.getName().toUri());
-    Assert.assertEquals(choice.getStrategy().toUri(), decoded.getStrategy().toUri());
+  @Test
+  public void testDecode() throws EncodingException {
+    StrategyChoice strategyChoice = new StrategyChoice(TestStrategyChoice);
+    assertEquals("/hello/world", strategyChoice.getName().toUri());
+    assertEquals("/some/non/existing/strategy/name", strategyChoice.getStrategy().toUri());
   }
 }