Protocol developers will often wish to provide a specific header type to be used in packets. Doing so allows a new protocol implementation to avoid overloading already-existing header fields. We consider a simplified version of RTP as an example. The RTP header will require a sequence number fields and a source identifier field. The following classes create the needed header (see ~ns/rtp.h and ~ns/rtp.cc):
{\rm From rtp.h:} /* rtp packet. For now, just have srcid + seqno. */ struct hdr_rtp { u_int32_t srcid_; int seqno_; /* per-field member functions */ u_int32_t& srcid() { return (srcid_); } int& seqno() { return (seqno_); } /* Packet header access functions */ static int offset_; inline static int& offset() { return offset_; } inline static hdr_rtp* access(const Packet* p) { return (hdr_rtp*) p-\>access(offset_); } }; {\rm From rtp.cc:} class RTPHeaderClass : public PacketHeaderClass { public: RTPHeaderClass() : PacketHeaderClass("PacketHeader/RTP", sizeof(hdr_rtp)) { bind_offset(&hdr_rtp::offset_); } } class_rtphdr; void RTPAgent::sendpkt() { Packet* p = allocpkt(); hdr_rtp *rh = hdr_rtp::access(p); lastpkttime_ = Scheduler::instance().clock(); /* Fill in srcid_ and seqno */ rh-\>seqno() = seqno_++; rh-\>srcid() = session_-\>srcid(); target_-\>recv(p, 0); } RTPAgent::RTPAgent() : session_(0), lastpkttime_(-1e6) { type_ = PT_RTP; bind("seqno_", &seqno_); }The first structure, hdr_rtp, defines the layout of the RTP packet header (in terms of words and their placement): which fields are needed and how big they are. This structure definition is only used by the compiler to compute byte offsets of fields; no objects of this structure type are ever directly allocated. The structure also provides member functions which in turn provide a layer of data hiding for objects wishing to read or modify header fields of packets. Note that the static class variable offset_ is used to find the byte offset at which the rtp header is located in an arbitrary ns packet. Two methods are provided to utilize this variable to access this header in any packet: offset() and access(). The latter is what most users should choose to access this particular header in a packet; the former is used by the packet header management class and should seldom be used. For example, to access the RTP packet header in a packet pointed by p, one simply says hdr_rtp::access(p). The actual binding of offset_ to the position of this header in a packet is done by routines inside ~ns/tcl/lib/ns-packet.tcl and ~ns/packet.cc. The const in access()'s argument provides (presumably) read-only access to a const Packet, lthough read-only is enforced since the return pointer is not const. One correct way to do this is to provide two methods, one for write access, the other for read-only access. However, this is not currently implemented.
IMPORTANT: Notice that this is completely different from the original (and obsolete) method to access a packet header, which requires that an integer variable, off_hdrname_, be defined for any packet header that one needs to access. This method is now obsolete; its usage is tricky and its misuse can be very difficult to detect.
The static object class_rtphdr of RTPHeaderClass../ns-2/rtp.cc is used to provide linkage to OTcl when the RTP header is enabled at configuration time. When the simulator executes, this static object calls the PacketHeaderClass constructor with arguments "PacketHeader/RTP" and sizeof(hdr_rtp). This causes the size of the RTP header to be stored and made available to the packet header manager at configuration time (see below, Section 12.2.4). Notice that bind_offset() MUST be called in the constructor of this class, so that the packet header manager knows where to store the offset for this particular packet header.
The sample member function []sendpkt method of RTPAgent creates a new packet to send by calling []allocpkt, which handles assignment of all the network-layer packet header fields (in this case, IP). Headers other than IP are handled separately. In this case, the agent uses the RTPHeader defined above. The Packet::access member function returns the address of the first byte in a buffer used to hold header information (see below). Its return value is cast as a pointer to the header of interest, after which member functions of the RTPHeader object are used to access individual fields.