14-10-2016, 11:30 AM
1458962185-NS2.docx (Size: 66.57 KB / Downloads: 4)
INTRODUCTION
During the last year, we have witnessed a lot of people asking for the same question in the ns users mailing list. How do I develop my own protocol for NS2? By writing this document we hope to help those researchers who need to add a manet routing protocol for NS-2.27. We focus our document in uncast protocols. The document is aimed to those people who are somehow familiar with performing simulations in ns-2, and they want to go one step forward to implement their own protocols. Everything we describe in this text is related to version 2.27 of NS2, but it might be useful for other versions as well. We assume that the reader is familiar with the NS2 basics. That means having read and at least mostly understood “Marc Greis’ Tutorial”. It would be very useful if you also take a look at “The ns Manual”, specially chapters 3-5, 10-12, 15-16, 23, 25 and 29. We will refer to them several times throughout this
text and encourage you to read them. Before coding your own routing protocol, you should know how to perform simulations with other implemented protocols and you are expected to feel familiar and comfortable with simulator. This will avoid lots of misunderstandings and doubts while reading this document. Besides this tutorial is about programming. You need knowledge about C++ and (a little) TCL programming. If you aren’t enough experienced with these programming languages, you should firstly read any of the excellent resources about them which are freely available in the Internet.
2 GETTING STARTED
We are going to implement a new manet routing protocol called protoname step by step. This protocol does nothing useful, but it is general enough to have several common points with other routing protocols. As you likely know (if not, please read firstly what we told you!) we are going to implement routing protocol using C++ and then we will do simulations describing scenarios with Tcl scripts. To allocate our code we will firstly create a new directory called protoname inside your NS2 base directory. We will create five files there: protoname.h This is the header file where will be defined all necessary timers (if any) and routing agent which performs protocol’s functionality. protoname.cc In this file are actually implemented all timers, routing agent and Tcl hooks. protoname pkt.h Here are declared all packets protoname protocol needs to exchange among nodes in the manet. protoname rtable.h Header file where our own routing table is declared. protoname rtable.cc Routing table implementation.
You can organize your code as you want to. That is, you can create more or less files, with those names or with others; this is only a hint. Our advice is to use at least those files and to create more as they are needed.
Now we have our “physical” structure (files), let’s continue with the “logical” one (classes). To implement a routing protocol in NS2 you must create an agent by inheriting from Agent class. At the very beginning of chapter 10 we can read “Agents represent endpoints where network-layer packets are constructed or consumed, and are used in the implementation of protocols at various layers”. As you can figure out, this is the main class we will have to code in order to implement our routing protocol. In addition, this class offers a linkage with Tcl interface, so we will be able to control our routing protocol through simulation scripts written in Tcl.
Our routing agent will maintain an internal state and a routing table (which is not always needed). Internal state can be represented as a new class or as a collection of attributes inside the routing agent. We will treat routing table as a new class, protoname rtable.
Also our new protocol must define at least one new packet type which will represent the format of its control packets. As we said these packet types are
defined in protoname/protoname pkt.h. When the protocol needs to send packets periodically or after some time from the occurrence of an event, it is very useful to count on a Timer class. We show an example in which we code our own timers for sending these packets at regular intervals. Timers are also useful in lots of other cases. Imagine protoname needs to store some sort of internal information which must be erased at a certain time. The best solution is to create a custom timer capable of doing such job. A timer should also be used to specify timelife of an entry in the routing table. In general, we will use a timer whenever we have to schedule a task at a given time.
There is another important class we must know before going into details. The Trace class is the base for writing log files with information about what happened during the simulation.
And the last hint for now: when you want to print a debug message in your code, it is helpful to use the debug() function as it is suggested in chapter 25. This allows you to turn debugging on or off from your simulation scripts and is easier to read for other programmers.
4.1 Tcl hooks
We saw in section 3 how to bind our own packet to Tcl. Now we will do the
same for our agent class. The aim is to let Protoname to be instantiated from
Tcl. To do so we must inherit from the class TclClass as depicted in the next
code.
protoname/protoname.cc
1: static class ProtonameClass : public TclClass {
2: public:
3: ProtonameClass() : TclClass("Agent/Protoname") {}
4: TclObject* create(int argc, const char*const* argv) {
5: assert(argc == 5);
6: return (new Protoname((nsaddr_t)Address::instance().str2addr(argv[4])));
7: }
8: } class_rtProtoProtoname;
The class constructor is in line 3 and it merely calls the base class with the string “Agent/Protoname” as an argument. This represents class hierarchy for this agent in a textual manner.
In ines 4-7 we implement a function called create() which returns a new Protoname instance as a TclObject. argv is of the form “<object’s name> <$self> <$class> <$proc> <user argument>” (see chapter 3 of [2] for more information). In this particular case it is “<object’s name> <$self> Agent/Protoname create-shadow <id>”. Because of this, at line 6 we return a new Protoname object with the identifier stated in argv[4]. We use the Address class to get a nsaddr t type from a string.
4.2 Timers
All we have to code in protoname/protoname.cc about timers is the expire() method. Timers are detailed in chapter 11 [2]. Implementing this is pretty easy because we only want to send a new control packet and to reschedule the timer itself. According to our design decisions these two tasks must be executed by the routing agent, so we invoke these callbacks as in the next example.
protoname/protoname.cc
1: void
2: Protoname_PktTimer::expire(Event* e) {
3: agent_->send_protoname_pkt();
4: agent_->reset_protoname_pkt_timer();
5: }
4.3 Agent
4.3.1 Constructor
Let’s begin with constructor implementation. As we can see in line 1 below, we start by calling the constructor for the base class passing PT PROTONAME as an argument. This constant will be defined later (see section 6) and it is used to identify control packets sent and received by this routing agent. In the same line we create our Protoname PktTimer object.
Just after that we bind accessible var as a boolean attribute which now may be read and written from Tcl. To bind this variable as an integer, we must use the bind() function instead of bind bool().
Line 3 saves the given identifier as the routing agent’s address.
protoname/protoname.cc
1: Protoname:rotoname(nsaddr_t id) : Agent(PT_PROTONAME), pkt_timer_(this) {
2: bind_bool("accessible_var_", &accessible_var_);
3: ra_addr_ = id;
4: }
Accessing from Tcl scripts is fairly simple. The next example sets the value
of accesible var to true.
simulation.tcl
1: Agent/Protoname set accesible_var_ true
4.3.2 command()
The next piece of code is a little bit more complicated. It consists of the implementation
of the command() method that our agent inherites from the Agent class.
protoname/protoname.cc
1: int
2: Protoname::command(int argc, const char*const* argv) {
3: if (argc == 2) {
4: if (strcasecmp(argv[1], "start") == 0) {
5: pkt_timer_.resched(0.0);
6: return TCL_OK;
7: }
8: else if (strcasecmp(argv[1], "print_rtable") == 0) {
9: if (logtarget_ != 0) {
10: sprintf(logtarget_->pt_->buffer(), "P %f _%d_ Routing Table",
11: CURRENT_TIME,
12: ra_addr());
13: logtarget_->pt_->dump();
14: rtable_.print(logtarget_);
15: }
16: else {
17: fprintf(stdout, "%f _%d_ If you want to print this routing table "
18: "you must create a trace file in your tcl script",
19: CURRENT_TIME,
20: ra_addr());
21: }
22: return TCL_OK;
23: }
24: }
25: else if (argc == 3) {
26: // Obtains corresponding dmux to carry packets to upper layers
27: if (strcmp(argv[1], "port-dmux") == 0) {
28: dmux_ = (PortClassifier*)TclObject::lookup(argv[2]);
29: if (dmux_ == 0) {
30: fprintf(stderr, "%s: %s lookup of %s failed\n",
31: __FILE__,
32: argv[1],
33: argv[2]);
34: return TCL_ERROR;
35: }
36: return TCL_OK;
37: }
38: // Obtains corresponding tracer
39: else if (strcmp(argv[1], "log-target") == 0 |
40: strcmp(argv[1], "tracetarget") == 0) {
41: logtarget_ = (Trace*)TclObject::lookup(argv[2]);
42: if (logtarget_ == 0)
43: return TCL_ERROR;
44: return TCL_OK;
45: }
46: }
47: // Pass the command to the base class
48: return Agent::command(argc, argv);
49: }
argv[0] contains the name of the method (always “cmd”, see chapter 3 [2]) being invoked, argv[1] is the requested operation, and argv[2..argc-1] are the rest of the arguments which were passed. Within this function we must code some mandatory operations as well as any other operation that we want to make accesible from Tcl. As an example we will code an operation called print rtable which dumps the contents of the routing table’s to the trace file.
We focus our code only in cases where we have two or three arguments, so that you can see how to process them. Each case must finish its execution returning either TCL OK (if everything was fine) or TCL ERROR (if any error happened).
Lines 4-7 describe a mandatory command that we always have to implement: start. The expected behaviour of this command is to configure the agent to begin its execution. In our case it starts its packet sending timer. We should implement here all the required actions that the routing agent must perform in order to begin its operation.