examples/networking/dtn/bplib_cla_udp/README.md
bplib is a DTN implementation by NASA.
What is DTN? Delay- or Disruption Tolerant Networking
The messages of the protocol (bundles) are sent over some underlying protocol, called a Convergence Layer (CL). The object that translates between the underlying protocol and the bundle processor is called a Convergence Layer Adapter (CLA).
CLAs exist for different protocols, this example shows the use of UDP as the CL protocol, bundles are sent directly as UDP payloads.
The special thing is that DTN does not require an end-to-end connection between two nodes and instead relies on store-and-forward. Each node should thus have a persistent storage to store bundles which cannot currently be forwarded, because there is no next hop.
This example was tested on the native target with gnrc UDP. It might also be usable
with 802.15.4 or nimble_netif to carry UDP, to test the communication on real hardware.
For direct communication over BLE, the BLE example exists.
To configure this example, the Makefile processes the REMOTE, REMOTE_EID_NODE
and LOCAL_EID_NODE flags.
REMOTE sets the underlying IPv6 remote address to send the bundles onREMOTE_EID_NODE sets the node number of the destination EIDLOCAL_EID_NODE sets the node number of the current deviceThe service number of the EID is statically chosen in the example, as is the UDP port (which by IANA considerations should use 4556 for BP/UDP anyways).
To test simple loopback, nothing needs to be changed, the flags will default
to the loopback. Simply compile with make all term. Make sure a tap device is
available, since it tries to bind to one anyways.
When using the bp send 0 "[DATA]" shell command, the data you entered goes though bplib's
internal delivery, since the current node is the destination. The data is never
passed through gnrc in this case.
The 0 here represents the channel to send data on, in this case we only use one channel, and indexing begins at 0.
To test with two devices create two tap devices as described in the gnrc examples.
Start both instances initially with make all term PORT=tap0 and
make all term PORT=tap1 and run the ifconfig shell command to find the IPv6
address of both virtual tap devices. Now that you know these, compile again to set
the remote addresses like so:
# Device A, has local address fe80::4832:95ff:feb7:5161
make all term PORT=tap0 REMOTE=fe80::d8af:c5ff:febc:a409 LOCAL_EID_NODE=100 REMOTE_EID_NODE=200
# Device B, has local address fe80::d8af:c5ff:febc:a409
make all term PORT=tap1 REMOTE=fe80::4832:95ff:feb7:5161 LOCAL_EID_NODE=200 REMOTE_EID_NODE=100
When running bp send 0 "[DATA]" now, the bundle should be delivered to the other instance.
Now, data goes through bplib, through gnrc, which sends the bundles as UDP payload.
The other device receives this and passes it to bplib again, which can deliver it,
since the channel is active
Since DTN is made for disruptions and thus employs a storage, this can be tested
as well in this example. On one (or both) of the nodes, run bp contact 0 0 to
set the contact 0 into the stopped state. In the normal startup in this example
it will get put into the started state. Now bplib must assume that the contact
cannot currently serve its destinations.
Bundles that are sent now with bp send 0 "[DATA]" will not be received by the
other device. Instead, they are placed into the storage, which on native is located
in the native subfolder of this example.
When this disruption is over, the contact can be re-enabled with bp contact 0 1.
After some timeout, the bundles will be pulled from storage and are received at
device B.
How such a disruption can be detected reliably is unfortunately not trivial. DTN originates from space communication, where contacts are schedulable from the static and predictable orbits, but with IoT devices this is generally not the case.
Using three devices would require larger changes to this example, however three BP nodes (on two devices) can be simulated by reconfiguration. Now device A wants to send a message to a different destination, which is neither A nor B currently:
# Device A. Note the remote node id changed
make all term PORT=tap0 REMOTE=fe80::d8af:c5ff:febc:a409 LOCAL_EID_NODE=100 REMOTE_EID_NODE=300
# Device B
make all term PORT=tap1 REMOTE=fe80::4832:95ff:feb7:5161 LOCAL_EID_NODE=200 REMOTE_EID_NODE=100
When sending a message from A now, nothing is received at B, because B's local node is not the specified destination. The message does get transmitted multiple times between the nodes though, because both nodes are configured to send all bundles with node ids 1 to 10000 to the other device.
This can be seen in Wireshark. The back and forth forwarding of the bundle here only stops, because the Hop Limit block is added and the bundle is deleted after the specified number of hops (10) is reached.
To now simulate the third node, stop the contact on device B with bp contact 0 0
and send a new bundle from A.
Now in Wireshark, only one bundle is sent and it is stored in the storage of B (native200/).
Now change device A such that it looks like it is a third device with the local node number of the destination that A had previously:
# Device A. Note the remote node id changed again
make all term PORT=tap0 REMOTE=fe80::d8af:c5ff:febc:a409 LOCAL_EID_NODE=300 REMOTE_EID_NODE=100
When the contact is now enabled again on device B (bp contact 0 1), node A, which
now is a different node in the bundle context, receives the bundle after it is pulled
from the storage of B.
This demonstrated the core idea of DTN, store-and-forward transfer without direct end-to-end paths. To forward between nodes without reconfiguration:
BPLIB_MAX_NUM_CONTACTS >= 2)