Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

vs. NFD (C++)

📝 Note: This is not a “ndn-rs is better” page. NFD is the reference implementation with over a decade of production deployment, a large research community, and de facto spec authority. This comparison explains the architectural differences and the tradeoffs each design makes, so you can choose the right tool for your deployment.

This page compares ndn-rs with NFD (NDN Forwarding Daemon) and its companion library ndn-cxx. NFD is the reference implementation of an NDN forwarder, written in C++ and developed by the NDN project team since 2014. Understanding the differences explains why ndn-rs exists and the tradeoffs it makes.

Comparison Table

AspectNFD / ndn-cxxndn-rsWhy ndn-rs chose differently
ArchitectureDaemon (nfd) + client library (ndn-cxx). Applications link against ndn-cxx and communicate with nfd over a Unix socket.Embeddable library. ndn-engine is a Rust library; applications can embed the forwarder in-process or run the standalone ndn-fwd binary.The daemon/client split forces IPC overhead on every packet and prevents co-optimization of application and forwarder logic. A library architecture lets the same crate run as a router, an embedded forwarder, or an in-app engine.
Pipeline safetyRuntime checks. Stages pass raw pointers or shared_ptr references; the pipeline relies on documentation and conventions to ensure stages do not use a packet after forwarding it.Ownership by value. PacketContext is moved through each PipelineStage. A stage that short-circuits consumes the context, making use-after-hand-off a compile error.C++ has no ownership transfer semantics that the compiler enforces. Rust’s move semantics give pipeline correctness for free, eliminating an entire class of bugs that NFD must catch through careful coding and review.
Data copy on CS hitRe-encodes on cache hit. When data is retrieved from the Content Store, NFD often re-encodes fields (e.g., updating the incoming face tag) before sending.Zero-copy Bytes. The CS stores the original wire-format Bytes (reference-counted). A cache hit sends the stored buffer directly – no re-encoding, no copy.Re-encoding is the dominant cost on cache hits. Storing wire-format bytes and using Bytes::clone() (which increments a reference count, not copies data) makes CS hits nearly free.
PIT concurrencyGlobal mutex. NFD’s PIT is protected by a single mutex; all pipeline processing is single-threaded within the forwarder.DashMap (sharded concurrent hash map). The PIT is partitioned into shards, each with its own lock. Multiple cores can process packets in parallel without contention.NDN PIT lookup is the hottest operation in the forwarder. A global mutex serializes all packet processing, limiting throughput to what one core can handle. Sharded locking scales with core count.
Strategy systemClass hierarchy. Strategies inherit from nfd::fw::Strategy (virtual methods). Adding a strategy requires C++ compilation and restarting the daemon.Trait composition + WASM. Strategies implement the Strategy trait. Built-in strategies are compiled in; external strategies can be loaded as WASM modules at runtime via ndn-strategy-wasm, with hot-swap and no restart.Class hierarchies are rigid – you cannot compose two strategies without writing a third class. Traits compose naturally (e.g., wrapping a strategy with a StrategyFilter). WASM allows deploying new forwarding logic to running routers without downtime.
Embedded supportSeparate project. ndn-lite is a completely separate C implementation for embedded devices, with its own packet format library and limited compatibility.Same crate, no_std. ndn-tlv and ndn-packet compile with no_std; ndn-embedded provides a minimal forwarder for bare-metal targets using the same types and encoding.Maintaining two separate codebases (NFD + ndn-lite) means divergent behaviour, duplicated bugs, and incompatible APIs. A single codebase with feature flags ensures embedded and full forwarders parse packets identically.
Face abstractionInheritance. Face types inherit from nfd::face::Face with virtual methods. Transport and link service are separate class hierarchies.Trait. Face is an async trait (recv() -> Bytes, send(Bytes)). Any type implementing the trait is a face. No inheritance, no link service split.The inheritance-based split between Transport and LinkService adds complexity without clear benefit for most transports. A flat trait is simpler, and Rust’s async trait support means faces are naturally non-blocking.
Packet parsingEager decode. ndn-cxx decodes all TLV fields upfront when constructing an Interest or Data object.Lazy decode via OnceLock. Fields are decoded on first access. A CS hit may never touch the nonce or lifetime fields.Eager decoding wastes cycles on fields the pipeline never reads. In the common case (cache hit or simple forwarding), only the name and type are needed. Lazy decode pays only for what is used.
Name representationndn::Name is a std::vector<Component> stored inline. Copying a name copies all components.Arc<Name> with SmallVec<[NameComponent; 8]>. Names are reference-counted and shared across PIT, FIB, CS, and pipeline without copying.NDN names appear in many data structures simultaneously. Copying a 6-component name on every PIT insert and FIB lookup adds up. Arc sharing eliminates these copies entirely.
Build systemwaf (Python-based). Complex dependency resolution, platform-specific patches, often requires manual intervention.Cargo. Standard Rust toolchain. cargo build compiles everything; cross-compilation to embedded targets works out of the box.Developer experience matters. Cargo’s dependency management, cross-compilation support, and reproducible builds reduce the barrier to contribution and deployment.

Where NFD Has the Advantage

NFD is the reference implementation with over a decade of production deployment and research use. Its advantages include:

  • Maturity: NFD has been tested in large-scale testbeds (NDN testbed, 30+ nodes worldwide) for years. ndn-rs is newer and less battle-tested.
  • Spec conformance: NFD defines the de facto standard for NDN forwarding behaviour. When the spec is ambiguous, NFD’s behaviour is the answer.
  • Ecosystem: Tools like ndnping, ndnpeek, ndncatchunks, and the NFD management protocol are widely used and well-documented. ndn-rs implements compatible tools (ndn-tools) and follows the NFD management protocol, but coverage is still growing.
  • Community: A larger developer and research community means more strategies, more faces, and more real-world deployment experience.

Migration Considerations

ndn-rs follows the NFD management protocol for router administration, so existing tooling (nfd-status, nlsrc) can interact with an ndn-rs router. The wire format is identical (NDN TLV), so ndn-rs nodes interoperate with NFD nodes on the same network. The main differences are in the internal API: applications using ndn-cxx’s C++ API need to be rewritten in Rust using ndn-app.