The NDN project is transitioning both the packet format for NDN and the forwarder codebase. To support comparison, testing, and transition of existing code, the NDN-CPP library supports both wire formats and both forwarders (in certain combinations described below). Support for NDNx and the ndnb wire format is deprecated and may be removed as early as Fall 2014, so we encourage all new applications to be written for the TLV wire format and NFD forwarder.

This document currently covers C++ language support of the Common Client Library (NDN-CPP) only, but will be updated to describe Python, Javascript, and Java libraries as well, which have more limited support for multiple wire formats and forwarders.

Using ndn-cpp with NDNx vs. NDNx-TLV vs. NFD

ndn-cpp is an NDN client library for C++. This document explains how it can be used with any of the three NDN forwarders NDNx, NDNx-TLV and NFD. (This assumes you have installed the latest ndn-cpp which can work with all the forwarders and wire formats.)

Each forwarder has different support for the wire format of ndnb (Binary XML) vs. TLV (type-length-value). Also, each forwarder has different requirements for a registerPrefix signed command interest (see "Using registerPrefix with NFD" below).

Forwarder ndnb (Binary XML) wire format? TLV wire format? registerPrefix signed command interest?
NDNx no

Writing your application based on the forwarder

Setting the ndnb wire format (for NDNx)

ndn-cpp will accept an interest or data packet in either ndnb or TLV. By default, ndn-cpp sends an interest or data packet in the TLV wire format. If your application connects to NDNx-TLV or NFD, you do not need to change your code.

However, if your application connects to NDNx, you need to use ndnb (Binary XML). There are two ways to use the ndnb (Binary XML) wire format. At the beginning of your application code, you can set the default wire format:

#include <ndn-cpp/encoding/binary-xml-wire-format.hpp>

Alternatively, when you use ./configure to build the ndn-cpp library, you can set the default wire format to ndnb (Binary XML):

./configure --enable-binary-xml=yes

Using registerPrefix with NFD

If you only have a consumer application which sends an interest and receives a data packet, and you connect to NDNx, then you only need to follow "Setting the ndnb wire format" above. If you have a publisher application which needs to receive an interest, then it needs to register a prefix with the forwarder. If your application connects to NDNx or NDNx-TLV, you don't need to change your code because the forwarder will accept the self-signed request which is sent by registerPrefix.

However, if your publisher application connects to NFD, it needs to send a signed command interest which is trusted by the forwarder. When you install NFD, it installs a default signing key on your system. For registerPrefix to create a signed command interest using this default signing key, your application needs to use the default KeyChain constructor and call setCommandSigningInfo so that the Face can sign the command interest created by registerPrefix:

Face face("localhost");
KeyChain keyChain;
face.setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());

(See the example application test-publish-async-nfd .) Note that the default KeyChain constructor will only succeed if NFD has installed the default signing key. If your system only has NDNx or NDNx-TLV, then the default KeyChain constructor will fail. In this case, you should use a KeyChain with a MemoryPrivateKeyStorage as shown in the example application test-publish-async-ndnx.


The above should let you make your application work with the different forwarders. Here are the details of how the ndn-cpp library interacts with the different wire formats and protocols.

Receiving a packet

When the library receives a data packet, it checks the first byte. If the first byte is 5 (Interest type code), 6 (Data type code) or 0x80, then it decodes the packet as TLV. Otherwise, it decodes it as ndnb (Binary XML). Therefore, you don't have to configure the library for receiving a packet since it automatically handles both TLV and ndnb.

Sending a register prefix command

For register prefix, NFD uses a signed commmand interest which has a different format than the register prefix message for NDNx and NDNx-TLV. As explained above, to use registerPrefix with NFD, your application must first call setCommandSigningInfo. Therefore, if your application has not called setCommandSigningInfo then the library assumes that you are connected to NDNx or NDNx-TLV and registerPrefix simply sends a self-signed request in the NDNx format, signing with a hard-wired private key. (NDNx only verifies the signature on the register prefix message using the public key in the message.) In this message, the internal ForwardingEntry and Data packet payload are encoded as ndnb (Binary XML) regardless of the supplied wire format. The outer register prefix interest packet is encoded using the supplied wire format, which is OK because NDNx-TLV will re-encode as ndnb if needed before processing.

However, if your application has called setCommandSigningInfo then registerPrefix uses the info to send a signed command command interest in the NFD format. If your application is connected to NFD then register prefix should succeed. But if your application is connected to NDNx or NDNx-TLV, then the forwarder will reject the request. In this case, the library gets an interest timeout and sends a self-signed request in the NDNx format as described above. Therefore, if your application calls setCommandSigningInfo, registerPrefix will work if connected to any of the forwarders.

Interest and Data API for TLV vs. ndnb

There are some differences in the Interest and Data packet wire format for TLV vs. ndnb (Binary XML), but the library uses one Interest class and one Data class to support both wire formats. Here are the details on how the API for ndnb (Binary XML) was changed to support TLV.

  • In MetaInfo, added get/setFreshnessPeriod. Deprecated get/setFreshnessSeconds. (This is simple factor of 1000 since FreshnessPeriod is milliseconds.)
  • In Interest, added get/setMustBeFresh. Deprecated get/setAnswerOriginKind. Internally, use the inverse of the ANSWER_STALE bit of answerOriginKind. When encoding TLV, give an error if other bits are set.
  • In the Interest constructor, set MustBeFresh true by default so that it has the same semantics as the default ANSWER_STALE = 0.
  • In Interest, added KeyLocator. Deprecated get/setPublisherPublicKeyDigest.

Here is how the fields of the Interest and Data object are treated when encoding as TLV instead of ndnb (Binary XML):


Field Treatment if encoding as TLV instead of ndnb (Binary XML)
name same
minSuffixComponents same
maxSuffixComponents same
publisherPublicKeyDigest Deprecated. If the interest needs a publisher public key digest, the keyLocator type is ndn_KeyLocatorType_KEY_LOCATOR_DIGEST.
KeyLocator if KeyLocator is specified and keyLocatorType is KEYNAME or PUBLISHER_PUBLIC_KEY_DIGEST, use it. Otherwise error.
exclude same
childSelector same
answerOriginKind Set MustBeFresh based on answerOriginKind ANSWER_STALE bit. Error if other anwerOriginKind bits are set.
scope same
interestLifetimeMilliseconds same
nonce If none or < 4 bytes, generate new random nonce bytes before issuing the interest. If > 4 bytes, truncate to 4. Deprecate setNonce in the API. Allow getNonce to show the nonce of an incoming Interest. If any Interest fields are changed, clear the nonce.


Field Treatment if encoding as TLV instead of ndnb (Binary XML)
name same
content same
MetaInfo.type Require BLOB, LINK or KEY, otherwise error.
MetaInfo.freshnessPeriod same
MetaInfo.timestamp ignore
MetaInfo.finalBlockID same
Signature.keyLocator if KeyLocator is specified and keyLocatorType is KEYNAME or PUBLISHER_PUBLIC_KEY_DIGEST, use it. Otherwise error.
Signature.signatureBits same
Signature.witness ignore. Deprecated get/setWitness.

Updated by Jeff Thompson over 5 years ago · 1 revisions