Rust implementation of dtn Bundle Protocol Version 7 (RFC 9171)
This library only handles encoding and decoding of bundles, not transmission or other processing of the data. A full daemon using this library can be found here: https://github.com/dtn7/dtn7-rs
Through the provided FFI interface, this library can also be used from C/C++, nodejs or flutter.
A simple benchmark is shipped with the library. It (de)serializes Bundles with a primary block, bundle age block and a payload block with the contents (b"ABC"
). This benchmark can be used to compare the rust implementation to the golang, python or java implementations.
cargo run --release --example benchmark
Finished release [optimized] target(s) in 0.29s
Running `target/release/examples/benchmark`
Creating 100000 bundles with CRC_NO: 510059 bundles/second
Creating 100000 bundles with CRC_16: 293399 bundles/second
Creating 100000 bundles with CRC_32: 291399 bundles/second
Encoding 100000 bundles with CRC_NO: 1090996 bundles/second
Encoding 100000 bundles with CRC_16: 436836 bundles/second
Encoding 100000 bundles with CRC_32: 432774 bundles/second
Loading 100000 bundles with CRC_NO: 564817 bundles/second
Loading 100000 bundles with CRC_16: 473768 bundles/second
Loading 100000 bundles with CRC_32: 462013 bundles/second
These numbers were generated on a MBP 13" 2018 with i5 CPU and 16GB of ram.
For debugging a small helper tool is shipped providing basic functionality such as:
- random bundle generation (as hex and raw bytes)
- encoding of standard bundles (as hex and raw bytes)
- decoding of bundles (from hex and raw bytes)
- exporting raw payload of decoded bundles
- time conversion helpers
Some examples are given in the following shell session:
$ cargo install bp7
[...]
$ bp7
usage "bp7" <cmd> [args]
encode <manifest> <payloadfile | - > [-x] - encode bundle and output raw bytes or hex string (-x)
decode <hexstring | - > [-p] - decode bundle or payload only (-p)
dtntime [dtntimestamp] - prints current time as dtntimestamp or prints dtntime human readable
d2u [dtntimestamp] - converts dtntime to unixstimestamp
rnd [-r] - return a random bundle either hexencoded or raw bytes (-r)
benchmark - run a simple benchmark encoding/decoding bundles
$ bp7 rnd
dtn://node81/files-680971330872-0
9f88071a000200040082016e2f2f6e6f646531382f7e74656c6582016e2f2f6e6f646538312f66696c657382016e2f2f6e6f646538312f66696c6573821b0000009e8d0de538001a0036ee80850a020000448218200085010100004443414243ff
$ bp7 decode 9f88071a000200040082016e2f2f6e6f646531382f7e74656c6582016e2f2f6e6f646538312f66696c657382016e2f2f6e6f646538312f66696c6573821b0000009e8d0de538001a0036ee80850a020000448218200085010100004443414243ff
[src/main.rs:101] &bndl = Bundle {
primary: PrimaryBlock {
version: 7,
bundle_control_flags: 131076,
crc: CrcNo,
destination: Dtn(
1,
DtnAddress(
"//node18/~tele",
),
),
source: Dtn(
1,
DtnAddress(
"//node81/files",
),
),
report_to: Dtn(
1,
DtnAddress(
"//node81/files",
),
),
creation_timestamp: CreationTimestamp(
680971330872,
0,
),
lifetime: 3600s,
fragmentation_offset: 0,
total_data_length: 0,
},
canonicals: [
CanonicalBlock {
block_type: 10,
block_number: 2,
block_control_flags: 0,
crc: CrcNo,
data: HopCount(
32,
0,
),
},
CanonicalBlock {
block_type: 1,
block_number: 1,
block_control_flags: 0,
crc: CrcNo,
data: Data(
[
65,
66,
67,
],
),
},
],
}
$ echo -e "source=dtn://node1/bla\ndestination=dtn://node2/incoming\nlifetime=1h" > /tmp/out.manifest
$ echo "hallo welt" | bp7 encode /tmp/out.manifest - -x
9f880700008201702f2f6e6f6465322f696e636f6d696e6782016b2f2f6e6f6465312f626c61820100821b0000009e8d137d23001a0036ee8085010100004c4b68616c6c6f2077656c740aff
$ bp7 decode 9f880700008201702f2f6e6f6465322f696e636f6d696e6782016b2f2f6e6f6465312f626c61820100821b0000009e8d137d23001a0036ee8085010100004c4b68616c6c6f2077656c740aff -p
hallo welt
The generated hex string can also be directly discplayed as raw cbor on the awesome cbor.me website, e.g. http://cbor.me/?bytes=9f88071a000200040082016e2f2f6e6f646531382f7e74656c6582016e2f2f6e6f646538312f66696c657382016e2f2f6e6f646538312f66696c6573821b0000009e8d0de538001a0036ee80850a020000448218200085010100004443414243ff
This library only handles encoding and decoding of bundles, not transmission or other processing of the data.
The library can be used as a shared library or statically linked into other apps.
With the help of cbindgen
(cargo install cbindgen
) the header file for this crate can be generated:
$ cbindgen -c cbindgen.toml > target/bp7.h
Example usages for Linux with C calling bp7
as well as nodejs can be found in examples/ffi
.
The library provides WebAssembly support and automatically builds JavaScript bindings when targeting any wasm32-*
platform.
Install wasm-pack
if you haven't already:
cargo install wasm-pack
Build for your target platform (see wasm-pack
documentation for details):
# For web browsers
wasm-pack build --target web --out-dir pkg-web
# For Node.js
wasm-pack build --target nodejs --out-dir pkg-node
The WASM module exports these functions (all return Result<T, JsValue>
for proper error handling):
Bundle Creation:
new_std_bundle_now(src, dst, payload)
- Create standard bundle with current timestamprnd_bundle_now()
- Create random bundle for testing
Encoding/Decoding:
encode_to_cbor(bundle)
- Encode bundle to CBOR bytesdecode_from_cbor(bytes)
- Decode CBOR bytes to bundle
Metadata Extraction:
bid_from_bundle(bundle)
/bid_from_cbor(bytes)
- Get bundle IDsender_from_bundle(bundle)
/sender_from_cbor(bytes)
- Get sender addressrecipient_from_bundle(bundle)
/recipient_from_cbor(bytes)
- Get recipient addresstimestamp_from_bundle(bundle)
/timestamp_from_cbor(bytes)
- Get creation timestamppayload_from_bundle(bundle)
/payload_from_cbor(bytes)
- Extract payload bytes
Validation:
valid_bundle(bundle)
/valid_cbor(bytes)
- Validate bundle structure and lifetimebundle_is_administrative_record(bundle)
/cbor_is_administrative_record(bytes)
- Check if bundle is administrative
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>bp7-rs WASM Example</title>
</head>
<body>
<script type="module">
import init, * as bp7 from './pkg-web/bp7.js';
async function run() {
await init();
// Create a bundle
const bundle = bp7.new_std_bundle_now(
"dtn://sender/app",
"dtn://receiver/app",
"Hello from the browser!"
);
// Extract metadata
const bundleId = bp7.bid_from_bundle(bundle);
const sender = bp7.sender_from_bundle(bundle);
const recipient = bp7.recipient_from_bundle(bundle);
const timestamp = bp7.timestamp_from_bundle(bundle);
const payload = bp7.payload_from_bundle(bundle);
const isAdmin = bp7.bundle_is_administrative_record(bundle);
console.log(`Bundle: ${bundleId}`);
console.log(`Route: ${sender} → ${recipient}`);
console.log(`Created: ${timestamp}`);
console.log(`Payload: "${new TextDecoder().decode(new Uint8Array(payload))}"`);
console.log(`Administrative: ${isAdmin}`);
// CBOR operations
const cborBytes = bp7.encode_to_cbor(bundle);
const isValid = bp7.valid_cbor(cborBytes);
console.log(`CBOR: ${cborBytes.length} bytes, valid: ${isValid}`);
// Decode and verify roundtrip
const decoded = bp7.decode_from_cbor(cborBytes);
const decodedId = bp7.bid_from_bundle(decoded);
console.log(`Roundtrip success: ${bundleId === decodedId}`);
}
run().catch(console.error);
</script>
</body>
</html>
const bp7 = require('./pkg-node');
async function example() {
// Create a bundle
const bundle = bp7.new_std_bundle_now(
"dtn://sender/app",
"dtn://receiver/app",
"Hello from Node.js!"
);
// Extract metadata
const bundleId = bp7.bid_from_bundle(bundle);
const sender = bp7.sender_from_bundle(bundle);
const recipient = bp7.recipient_from_bundle(bundle);
const timestamp = bp7.timestamp_from_bundle(bundle);
const payload = bp7.payload_from_bundle(bundle);
const isAdmin = bp7.bundle_is_administrative_record(bundle);
console.log(`Bundle: ${bundleId}`);
console.log(`Route: ${sender} → ${recipient}`);
console.log(`Created: ${timestamp}`);
console.log(`Payload: "${new TextDecoder().decode(new Uint8Array(payload))}"`);
console.log(`Administrative: ${isAdmin}`);
// CBOR operations
const cborBytes = bp7.encode_to_cbor(bundle);
const isValid = bp7.valid_cbor(cborBytes);
console.log(`CBOR: ${cborBytes.length} bytes, valid: ${isValid}`);
// Decode and verify roundtrip
const decoded = bp7.decode_from_cbor(cborBytes);
const decodedId = bp7.bid_from_bundle(decoded);
console.log(`Roundtrip success: ${bundleId === decodedId}`);
}
example().catch(console.error);
For server-side WASI environments, install the WASI runtime:
cargo install wasmtime-cli
Build and run WASI applications:
# Build for WASI
cargo build --target wasm32-wasip1 --release --example benchmark
# Run with wasmtime
wasmtime run target/wasm32-wasip1/release/examples/benchmark.wasm
Example WASI benchmark performance:
Creating 100000 bundles with CRC_NO: 511956 bundles/second
Creating 100000 bundles with CRC_16: 147850 bundles/second
Creating 100000 bundles with CRC_32: 145072 bundles/second
Encoding 100000 bundles with CRC_NO: 1072625 bundles/second
Encoding 100000 bundles with CRC_16: 450680 bundles/second
Encoding 100000 bundles with CRC_32: 447624 bundles/second
Loading 100000 bundles with CRC_NO: 520731 bundles/second
Loading 100000 bundles with CRC_16: 231351 bundles/second
Loading 100000 bundles with CRC_32: 230073 bundles/second
For WASM targets, the library provides a wasm-js
feature flag that enables proper randomness support via getrandom's wasm_js
backend. Following getrandom's recommendations, this feature is included in the default feature set for convenience, but can be disabled for library users who want to choose their own getrandom
backend.
If you use this software in a scientific publication, please cite the following paper:
@INPROCEEDINGS{baumgaertner2019bdtn7,
author={L. {Baumgärtner} and J. {Höchst} and T. {Meuser}},
booktitle={2019 International Conference on Information and Communication Technologies for Disaster Management (ICT-DM)},
title={B-DTN7: Browser-based Disruption-tolerant Networking via Bundle Protocol 7},
year={2019},
volume={},
number={},
pages={1-8},
keywords={Protocols;Browsers;Software;Convergence;Servers;Synchronization;Wireless fidelity},
doi={10.1109/ICT-DM47966.2019.9032944},
ISSN={2469-8822},
month={Dec},
}
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in bp7-rs by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.