In this tutorial, you'll learn how to use the programs in the apps directory to send data of various kinds through your DTN. You'll get to see more things happening if you've followed the instructions in Building a Bigger DTN first, but even with a standalone dtnd, you can send and receive bundles.
The most basic tools to send bundles through the DTN network are dtnsend and dtnrecv. You can use dtnsend to create a bundle and set various options in it. The bundle will move through the DTN until it gets to the destination node. You'll then use dtnrecv to retrieve the bundle out of the final dtnd.
First, take a look at the options dtnsend takes:
$ DTN2/apps/dtnsend/dtnsend -h usage: dtnsend [opts] -s <source_eid> -d <dest_eid> -t <type> -p <payload> options: -s <eid|demux_string> source eid) -d <eid|demux_string> destination eid) -r <eid|demux_string> reply to eid) -t <f|t|m|d> payload type: file, tmpfile, message, or date -p <filename|string> payload data -e <time> expiration time in seconds (default: one hour) -i <regid> registration id for reply to -n <int> copies of the bundle to send -z <time> msecs to sleep between transmissions -c request custody transfer -C request custody transfer receipts -D request for end-to-end delivery receipt -X request for deletion receipt -R request for bundle reception receipts -F request for bundle forwarding receipts -w wait for bundle status reports -E <int> include extension block and specify type -P <int> flag value(s) to include in extension block -S <string> extension block content
To use dtnsend, you need to give it a source and destination. Then you tell it what kind of data to send, and where to find it. You may use other command line options to set some optional flags in the bundle.
First, let's send in a little test message that we will be able to see inside the daemon, and then will be able to fetch with dtnrecv.
$ DTN2/apps/dtnsend/dtnsend -s dtn://dtn-a.dtn/me -d dtn://dtn-a.dtn/testing -t m -p "testing"
You can use a command like that, but instead of using the example hostname dtn-a, use the local eid you configured in your dtn.conf file.
In the window where dtnd is running, type "bundle list". You should get something like this:
dtn% bundle list Currently Pending Bundles (1): 0 : dtn://dtn-a.dtn/me -> dtn://dtn-a.dtn/testing length 7
There's a couple things to note here. First, see how the bundle is shown with length 7? That's what we'd expect, because we sent a payload from the command line of the letters "testing". The bundle consists of exactly those 7 letters. There's no terminating NUL character, nor a newline. Second, notice that our eids now have an extra string on them, in contast to the local eid we set in the configuration file in the first tutorial. It's easiest to think of these as the equivalent of TCP ports. For more details on how addressing works in the DTN world, see the architecture section of the manual.
In another window, go take a look behind the scenes at the directory where you told dtnd to store the bundles in transit. You should find a file there named bundle_0.dat. It should be no surprise by now that this file is 7 bytes long, and contains the string "testing". Generally, you'll never need to do anything with the files in this directory. They are managed by dtnd for you. But it's always nice to have an idea what's going on, so now you know.
So now we've gotten a bundle into the system, and the bundle has made it's way across the DTN to its final destination. "Big deal", you say, "the final destination is the same place we put it in!". Ok, you've got a point. But later we'll try something fancier and with some imagination, you'll be able to pretend you've sent a bundle all the way to Mars. Before that though, we've got to get our bundle back out. And for that, we use dtnrecv.
The usage of dtnrecv is much easier to remember than that of dtnsend. You only need to tell it the name of the endpoint who's bundles you want to pick up. Because dtnrecv cannot pick up bundles from across the DTN, but only on the local node, it's options are much simpler.
$ DTN2/apps/dtnrecv/dtnrecv dtn://dtn-a.dtn/testing dtnrecv (pid 29614) starting up find registration succeeded, regid 0 register succeeded, regid 13 looping forever to receive bundles dtn_recv [dtn://dtn-a.dtn/testing]... 7 bytes from [dtn://dtn-a.dtn/me]: transit time=0 ms 0000000 7465 7374 696e 67 | testing dtn_recv [dtn://dtn-a.dtn/testing]... Ctrl-C
As before, there's a couple of things to note here. First, notice how dtnrecv was able to figure out the local eid for the local dtnd, and them add the argument onto the end to make the complete eid. It did that by asking the local server what its local eid is. The second thing to notice is that the answer did indeed come back correctly. The 7 bytes we put into the DTN with dtnsend came back out with dtnrecv. Finally, note that dtnrecv stayed running until we killed it with Ctrl-C. It was waiting for more bundles for that endpoint to arrive.
Turn your attention to the window where dtnd is running. Type "bundle list" again and notice that there are no currently pending bundles. Great! The server did it's job and held the bundle until an application registered and requested that bundles for that endpoint get delivered. This is a key difference between sending data with TCP or UDP and sending data over a DTN. In a DTN, every part of the network, from the link protocols up to the applications themselves need to recognize and tolerate delays and disruptions. Typically applications do this by operating asynchronously. Another way applications can operate is by arranging with the DTN bundle router to make a callback to the application when bundles arrive. Stay tuned for a future tutorial showing how that works when I figure it out myself! -jra
In the example above, we used the "-t m" parameter to indicate we wanted to specify the bundle contents from the command line. But what if you wanted to send a movie through a DTN? You'd want to use "-t f" to indicate you are sending a file, then the argument following "-p" will be interpreted as a filename. dtnd will open that file and read it in to fill up the bundle. Be aware that dtnd needs to make a copy of your file and put it in the bundle directory, even if your file is quite big. Once dtnd has gotten another server someplace else in the DTN to take custody of the bundle, it will be deleted from the local server. (However, custody transfer is not currently implemented in DTN2.)
The final payload type is "d", for date. When you use this, you do not need -p. A payload consisting of the date is made up automatically for you. This is useful for testing, but not for much else.
If you add the -e and -w options to the dtnsend command above, something different happens. The -e argument asks the far end DTN server (in this case, still our local server) to make up a receipt and send it via the DTN back to our reply address. Because we did not use the -r argument to explicitly set a reply address, the receipt will come to the source eid we specified with the -s option. The -w argument tells dtnsend to wait for incoming messages to the reply endpoint. So, here's an example of it in action:
$ DTN2/apps/dtnsend/dtnsend -e -w -s dtn://dtn-a.dtn/me -d dtn://dtn-a.dtn/testing -t m -p "testing" got 67 byte report from [dtn://dtn-a.dtn]: time=18893.0 ms
18.893 seconds?!? What kind of broken network could possibly take 18.893 seconds to deliver a little 7 byte bundle? What's going on here?
Well, recall that this is a disruption tolerant network, and that the application on the other end may not have been running at the time we sent the message. In fact it was not. The 18 seconds was how long it took me to find another window and run dtnrecv. (So I type slow. Sue me.) The dtnd server couldn't create and send a receipt until it had delivered the bundle to the final destination, which in this case was the dtnrecv process that I started a few seconds after I ran dtnsend. If I'd had my dtnrecv up and running before I ran dtnsend the receipt would have arrived in just a few milliseconds.
What if you wanted to move a file to another node on the DTN? You've have to have someone logged into that machine ready to run dtnrecv on it. And of course, you can't log in to it, because the other bundle router is on the other side of Mars. And of course, you can't put your buddy there to run the command, because, well, it's on the other side of Mars. What to do? If there was a daemon there listening for bundles arriving on a certain endpoint, and you had a program that could package up local files and send them to the endpoint the far side was waiting on, you could move files!
That's exactly how dtncp and dtncpd work. dtncp is used to pick up a file from the local filesystem send it as a bundle. dtncpd is the daemon, which would stay running all the time. It registers with the local bundle router requesting a notification when a bundle comes in that was sent by dtncp. It copies the bundle into an "incoming files" directory. Voila, you've copied a file.
You need to run a dtncpd on every node in the network where you want to be able to copy files. If a node is only sending out files, it need not have a dtncpd running.
Note that dtncpd is not a general purpose file copy system like scp. It can only deposit files into a specific directory. It was written this way because it is an example application, and implementing the security-critical parts of a general file transfer utility were beyond the scope of the example. Likewise, there's no facility to run remote commands on the remote node. It would be possible to add some kind of flag to the bundle contents specifying whether or not to run the received file (and with which arguments). These are all possibilities for bundle applications you might choose to make in the future.
In one window, we get a directory ready for dtncpd to put files into, then start it. dtncpd registers on the endpoint /dtncp/recv:*, then waits for incoming files.
$ mkdir /home/fred/dtn/incoming $ DTN2/apps/dtncpd/dtncpd /home/fred/dtn/incoming opening connection to dtn router... local_eid [dtn://dtn-a.dtn/dtncp/recv:*] dtn_register succeeded, regid 0xa dtn_recv [dtn://dtn-a.dtn/dtncp/recv:*]...
In another window, we copy a file:
$ DTN2/apps/dtncp/dtncp /etc/motd dtn://dtn-a.dtn
And in the first window, dtncpd says:
====================================== File Received at Sun Mar 6 01:03:24 2005 region : internet host : dtn-a path : file : motd size : 960 bytes loc : /home/fred/dtn/incoming/dtn-a//passwd
You may also give dtncp a third argument, which will be the filename on the far side. The default, if you neglect to provide this argument, is to make the far side file have the same name as the local file.
The DTN2 implementation treats * (asterisk) as a special character in endpoints. The asterisk denotes that any endpoint that matches the prefix before the asterix will match that endpoint. dtncpd uses a wildcard endpoint to be able to match the endpoints that dtncp makes by adding the desired remote filename onto the end of the first part of the endpoint string, /dtncp/recv:.