bundle #
An OCaml implementation of the Bundle Protocol Version 7 (RFC 9171), the store-and-forward protocol of Delay-Tolerant Networking (DTN).
A link to a spacecraft, a sensor in a remote field or a network after a disaster may be down for hours, with round trips measured in minutes. TCP/IP assumes an end-to-end path exists at the moment a packet is sent, and fails on such links. The Bundle Protocol instead moves self-contained messages, called bundles, from node to node: each node stores a bundle until the next hop is reachable, then forwards it. NASA runs DTN on the International Space Station, and CCSDS 734.20-O-1 profiles version 7 of the protocol for space missions.
This package provides the bundle codec and the Bundle Protocol Agent of RFC 9171
Section 5 as a pure state machine, with no I/O. The Bundle Protocol Agent is
the part of a node that implements the protocol: it accepts bundles from local
applications and from convergence layers, decides for each one whether to
deliver it locally, forward it or delete it, and keeps it in storage while no
next hop is reachable. A convergence layer is the adapter that carries whole
bundles between two neighbouring nodes over one underlying transport (a TCP
connection, UDP, a space link); the Bundle Protocol above it never sees that
transport. The companion package
bundle-eio runs that state machine over a real network with Eio.
Features #
- An RFC 9171 bundle codec, over CBOR (RFC 8949) with
nox-cbor, and streaming I/O withbytesrw. - The standard block types (payload, previous node, bundle age and hop count), with unknown extension blocks kept as raw bytes.
- CRC-16 X.25 and CRC-32C block integrity checks. Each block is a CBOR array
whose last element is its CRC, and the block's
crc_typesays whether it carries none, 2 bytes (CRC-16 X.25) or 4 bytes (CRC-32C). The sender computes the CRC over the block's whole CBOR encoding with that last element's bytes set to zero, then writes it in; the receiver recomputes it the same way and refuses the block on a mismatch (RFC 9171 Section 4.2.1). - Endpoint identifiers in the
dtn:andipn:schemes. - Administrative records (RFC 9171 Section 6.1): the
[type code, content]array, with the bundle status report of Section 6.1.1 decoded field by field and every other record type, including the private-use codes RFC 9713 reserves, carried as its code and CBOR content. - The Bundle Protocol Agent (RFC 9171 Section 5) as a pure transition from the current time and an event to a new state and a list of actions: dispatch, local delivery with reassembly of fragments (Section 5.9), forwarding with the Previous Node, Bundle Age and hop count blocks updated (Section 5.4), and lifetime expiry counted from the creation time (Section 5.5).
- An Eio driver, in the
bundle-eiopackage, with a TCPCLv4 (RFC 9174) convergence layer: it opens a TCP session, negotiates the session parameters with the peer, and sends each bundle as length-prefixed segments that the receiver acknowledges.
Installation #
Install with opam:
$ opam install bundle bundle-eio
If opam cannot find the packages, they may not yet be released in the public
opam-repository. Add the overlay repository, then install them:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install bundle bundle-eio
Usage #
Create a bundle, encode it and decode it back:
(* Create a bundle *)
let bundle =
Bundle.v
~source:(Bundle.Ipn (1L, 1L))
~destination:(Bundle.Ipn (2L, 1L))
~creation_timestamp:{ Bundle.time = 0L; seq = 0L }
~payload:"Hello, DTN!"
()
(* Encode to bytes *)
let encoded = Bundle.encode bundle
(* Decode from bytes *)
let () =
match Bundle.decode encoded with
| Ok decoded ->
Printf.printf "Payload: %s\n" (Option.get (Bundle.payload decoded))
| Error e -> Fmt.pr "Error: %a@." Bundle.pp_error e
Running a node with Eio #
Bundle.Node decides what happens to each bundle: Bundle.Node.incoming maps
the current DTN time and an event (a received bundle, a local request to send
one, a concluded send, an expired lifetime timer) to a new state and a list of
actions. Because time arrives as an input, the same state machine runs under
Eio, in a unikernel and in the deterministic test suite.
The bundle-eio package carries those actions out. Bundles cross a convergence
layer, lifetime timers run on the Eio clock, and service events reach the
application as they happen. The convergence layer is a functor argument, and
Bundle_eio.Tcpcl provides TCPCLv4 over an
ocaml-tcpcl session. The
node's configuration names its local endpoint, for example
Bundle.Node.config ~local_eid:(Bundle.Dtn "node") ():
let run_node ~sw ~clock ~mono ~net ~peer ~config bundle =
match Tcpcl_eio.connect ~sw ~clock:mono ~net peer with
| Error e -> Fmt.failwith "%a" Tcpcl_eio.pp_error e
| Ok session ->
let agent = Bundle_eio.Tcpcl.v ~sw ~clock session config in
Bundle_eio.submit agent bundle;
Bundle_eio.receive agent
In a MirageOS unikernel, the Dtn device of
nox-mirage.eio exposes the same
agent.
API #
Bundle provides:
- the types
Bundle.t(a bundle),Bundle.eid(an endpoint identifier:dtn:none, adtn:URI oripn:node.service),Bundle.primary_block,Bundle.canonical_blockandBundle.timestamp; Bundle.vto create a bundle,Bundle.encodeandBundle.decodeto convert it to and from bytes, andBundle.readto read one from a CBOR decoder;Bundle.payload,Bundle.previous_node,Bundle.ageandBundle.hop_countto read the standard blocks;Bundle.bundle_id, the identity of a bundle (RFC 9171 Section 4.2), andBundle.status_report_reason, the reason codes of RFC 9171 Table 1;Bundle.Node, the agent state machine:Bundle.Node.configandBundle.Node.vcreate one, andBundle.Node.incomingapplies an event;Bundle.Event, the service events the agent reports (received, delivered, forwarded, deleted).Bundle.Admin_record, the administrative record codec and its bundle status report (RFC 9171 Section 6.1).
Bundle_eio provides the running agent, with submit, receive,
next_event and stored, and the CONVERGENCE_LAYER signature with its
Make functor.
Related work #
- ION is the DTN implementation in C from NASA JPL, used on flight missions.
- µD3TN is a small C implementation for embedded systems.
- DTN7 provides BPv7 implementations in Go and Rust.
This implementation is written in OCaml with few C dependencies, and uses
bytesrw and
Eio. Its protocol core has no I/O, so
one state machine serves the Eio driver, the unikernel device and the tests.
Bundle Protocol Security (RFC 9172 and RFC 9173) is the separate bpsec
package, which works over the same Bundle.t.
The node reassembles fragments addressed to it (RFC 9171 Section 5.9) but never fragments a bundle (Section 5.8, which leaves fragmenting to the node's discretion), generates no bundle status reports (Section 6.1.1, a SHOULD), and never retries a forward whose sending failed (Section 5.4, Step 5 leaves the retry to the implementation).
References #
- RFC 9171, Bundle Protocol Version 7
- RFC 9174, Delay-Tolerant Networking TCP Convergence-Layer Protocol Version 4
- RFC 9172, Bundle Protocol Security (BPSec)
- CCSDS 734.20-O-1, CCSDS Bundle Protocol Specification, the experimental CCSDS profile of RFC 9171 (Orange Book, April 2025)
Licence #
ISC License. See LICENSE.md for details.