To transfer informations from one node to another, packets are sent over the channel. In the next section it will be described how to add custom data to the packets.
The ns2 class packet
provides methods to access the various fields of a packet. It has the setdata()
function that allows you to write in the data field of the packet, but the type of data you can write is AppData, and is very problematic to use expecially when using the copy()
method. We will not spend more time on it but we'll focus on a more useful way of data transportation.
Usually when you have to add some data to a packet you can add a field on existing headers or you can create your own header.
The first method is very simple and require small modification to the code, the second is al little bit more complicated.
Adding a new field on an existing header is possible adding the definition of the new field in the code where the header is defined. This is often positioned at the beginning of header files. Header of packet are defined as a typedef struct
. Your field can be inserted adding you data in the list preceded by the data type (e.g int numberOfPacketTransmitted_
). If you want, you can add a specific function allowing you to access that specific field. These functions are declared inline
and simply return a pointer to the field itself.
The creation of a personal header extends the procedure just seen. First thing to do is to create the packet header structure at the beginning of header (.h) file. Follow this example:
typedef struct hdr_NC { int len_; /// \brief Lenght of the data field long gen_; /// \brief Generation of the packet double time_; /// \brief Timestamp of the generation of the packet // Methods to access to the header fields inline long& generation() { return (gen_);} inline int& length() { return (len_);} inline double& timestamp() { return (time_);} // Access to the NC header static int offset_; inline static int& offset() { return offset_; } inline static struct hdr_NC* access(const Packet* p) { return (struct hdr_NC*)p->access(offset_); } } hdr_NC;As you can see, you have to add some lines at the end of the struct code. These lines are fundamental for the correct packet use. These metods provide a reference to the offset of header whithin the packet and a method to access the header structure, as we'll see in next paragraphs.
To make your header a valid header also for ns2 you have to decalre a class which inherits from PacketHeaderClass
. Usually this code is added in the initlib.cc file. With this definition, you make possible the creation of a header of the same size of the struct defined before. Using the access method defined in the struct you will able to access every single field of the header. The scheme is as follows, don't forget to define the offset and to include the header file where you defined the struct!
#include <tclcl.h> #include "NCR.h" int hdr_NC::offset_ = 0; static class NCHeaderClass : public PacketHeaderClass { public: NCHeaderClass() : PacketHeaderClass("PacketHeader/NC", sizeof(hdr_NC)) { bind_offset(&hdr_NC::offset_); } } class_hdrNC;The file continues creating the header which will be added as overhead of packets
... extern "C" int Miraclencr_Init() { NCHeaderClass* nc = new NCHeaderClass; // <--- nc->bind(); // <--- InitTclCode.load(); return 0; }The last operation you have to do is to put the following string at the beginning of the file that set up default values of variables and/or provides description for Tcl procedures (for details on this kind of file, see Section 8). This string makes header ``active'' (i.e. tells to the simulator to reserve some memory space for it).
PacketHeaderManager set tab_(PacketHeader/NC) 1This parameter is very important and often is a source of errors. Without setting
tab_
you overwrite other header fields!
After the library compilation, you can see that (usually) in initTcl.cc file the new string is at the top of the file.
In this paragraph it will be explained how to manage the packet header in your methods. First thing to do is createing the packet at the sender side, allocating some memory space:
Packet* p = Packet::alloc();This allocation is needed only if you are creating a new packet. In most of methods, you will refer to an existing pointer of a packet previously declared, such as those pointed by
recv(Packet* p)
methods.
Then access the header and modify the field you are interested in:
hdr_cmn* ch = hdr_cmn::access(p); ch->uid() = uidcnt_++; ch->ptype() = PT_MCBR; ch->size() = pktSize_;In this case we acessed the common header, which is a general scope header. We modified the sequence number, the packet type and packet size.
At the receiver side we manage incoming packet with the same procedure. We access a particular header for first and then we use one of the methods implemented to retreive the values of each field.
hdr_ip* iph = hdr_ip::access(p); if (iph->saddr() != dstAddr_) { drop(p,1,CBR_DROP_REASON_WRONG_SADDR); }In this example we accessed IP header and then we checked the coherence of reception, evaluating if the packet is from the right sender. In this case the source addres is recovered by
iph->saddr()
.
In one of the previous lines we changed the packet type, but we didn't expalined how to define a new one.
The first step is to define a new packet type, adding the string packet_t PT_MCBR;
at the beginning of (usually) the initlib.cc.
Then, in the same file you have to add the new packet type to the list of known packets in ns2. This is done by a simple procedure:
extern "C" int Miraclencr_Init() { ... PT_NC = p_info::addPacket("NC"); }This procedure automatically updates the list of packet types in ns2 code.
When you exploit the newly defined packet type, remember to insert at the beginning of the code you are going to use the call to the previously defined packet type writing extern packet_t PT_MCBR;
, so the program knows thath definition is provided in an external file.