1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
|
\chapter{Packet Headers and Formats}
\label{chap:pformat}
The procedures and functions described in this chapter can be found in
\nsf{tcl/lib/ns-lib.tcl},
\nsf{tcl/lib/ns-packet.tcl}, and \nsf{packet.\{cc, h\}}.
Objects in the \clsref{Packet}{../ns-2/packets.h} are the fundamental unit of
exchange between objects in the simulation.
The class \code{Packet}
provides enough information to link a packet on to a list
(\ie, in a \code{PacketQueue} or on a free list of packets),
refer to a buffer containing packet headers
that are defined on a per-protocol basis,
and to refer to a buffer of packet data.
New protocols may define their own packet headers or may extend
existing headers with additional fields.
New packet headers are introduced into the simulator
by defining a C++ structure with the needed fields,
defining a static class to provide OTcl linkage,
and then modifying some of the simulator initialization code
to assign a byte offset in each packet where the new header
is to be located relative to others.
When the simulator is initialized through OTcl,
a user may choose to enable
only a subset of the compiled-in packet formats, resulting in
a modest savings of memory during the execution of the simulation.
Presently, most configured-in packet formats are enabled.
The management of which packet formats are currently enabled
in a simulation is handled by a special packet header manager
object described below.
This object supports an OTcl method used to specify
which packet headers will be used in a simulation.
If an object in the simulator makes use of a field in a header
which has not been enabled, a run-time fatal program abort occurs.
\section{A Protocol-Specific Packet Header}
\label{sec:ppackethdr}
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 \nsf{rtp.h} and \nsf{rtp.cc}):
\begin{program}
{\rm From rtp.h:}
/* {\cf rtp packet. For now, just have srcid + seqno.} */
struct hdr_rtp \{
u_int32_t srcid_;
int seqno_;
/* {\cf per-field member functions } */
u_int32_t& srcid() \{ return (srcid_); \}
int& seqno() \{ return (seqno_); \}
/* {\cf 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();
/* {\cf 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_);
\}
\end{program}
The first structure, \code{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 \code{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: \code{offset()} and \code{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
\code{p}, one simply says \code{hdr_rtp::access(p)}.
The actual binding of \code{offset_} to the position of this header in
a packet is done by routines inside \nsf{tcl/lib/ns-packet.tcl} and
\nsf{packet.cc}.
The \code{const} in \code{access()}'s argument provides (presumably)
read-only access to a \code{const} Packet, lthough read-only is
enforced since the return pointer is not \code{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.
{\bf IMPORTANT}: Notice that this is completely different from the
{\em original} (and obsolete) method to access a packet header, which
requires that an
integer variable, \code{off_\tup{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.
% which is accomplished by declaring and binding the integer variable
% \code{off_\tup{hdrname}_}
% where \code{\tup{hdrname}} refers to a shorthand name
% of the header of interest which must match the
% name assigned in \nsf{tcl/lib/ns-packet.tcl}.
% This is performed above by the RTPAgent's constructor.
% Generally, one header object for each type of header
% in the simulation is instantiated at simulator run-time.
% A particular header is enabled via OTcl in the simulation during
% simulator configuration time (see Section~\ref{sec:configpacket}).
The static object \code{class_rtphdr} of
\clsref{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 \code{PacketHeaderClass} constructor with arguments
\code{"PacketHeader/RTP"} and \code{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~\ref{sec:packethdrmgr}).
Notice that \code{bind_offset()} {\bf 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 \fcn[]{sendpkt} method
of \code{RTPAgent} creates a new packet
to send by calling \fcn[]{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 \code{RTPHeader} defined above.
The \fcn{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 \code{RTPHeader}
object are used to access individual fields.
\subsection{Adding a New Packet Header Type}
Assuming we wish to create a new header called \code{newhdr}
the following steps are performed:
\begin{enumerate}\itemsep0pt
\item create a new structure defining the raw fields
(called \code{hdr_newhdr}), define \code{offset_} and access
methods.
\item define member functions for needed fields.
\item create a static class to perform OTcl linkage
(defines \code{PacketHeader/Newhdr}), do \code{bind_offset()}
in its constructor.
\item edit \nsf{tcl/lib/ns-packet.tcl} to enable new packet format
(see \ref{sec:pinfoclass}, \ref{sec:configpacket}).
\end{enumerate}
{\em This is the recommended way to add your packet headers.
If you do
not follow this method, your simulation may still work, but it may
behave in a unpredictable way when more protocols are added into
your simulation.
The reason is that the BOB (Bag of Bits,
Section~\ref{sec:packetclass}) in \ns packet is a large
sparse space, assigning one wrong packet header offset may not
trigger failure immediately.}
\subsection{Selectively Including Packet Headers in Your Simulation}
By default, ns includes {\em ALL} packet headers of {\em ALL}
protocols in ns in {\em EVERY} packet in your simulation.
This is a LOT of overhead, and will increase as more
protocols are added into ns.
For ``packet-intensive'' simulations, this could be a huge overhead.
For instance, as of now (Aug 30, 2000), the size of packet headers of
all protocols in ns is about 1.9KB; however, if you turn on only the
common header, the IP header and the TCP header, they add up to about
100 bytes.
If you are doing large-scale web traffic simulation with many big fat
pipes, reducing unused packet headers can lead to major memory
saving.
To include only the packet headers that are of interest to you in your
specific simulation, follow this pattern (e.g., you want to remove AODV
and ARP headers from your simulation):
\begin{program}
remove-packet-header AODV ARP
......
set ns [new Simulator]
\end{program}
Notice that \code{remove-packet-header} MUST go before the simulator
is created.
All packet header names are in the forms of
\code{PacketHeader/[hdr]}.
You only need to supply the \code{[hdr]} part, not the prefix.
To find the names of packet headers, you may either look them up in
\nsf{tcl/lib/ns-packet.tcl}, or run the following simple commands in
\ns:
\begin{program}
foreach cl [PacketHeader info subclass] \{
puts $cl
\}
\end{program} %$
To include only a specific set of headers in your simulation, e.g., IP
and TCP, follow this pattern:
\begin{program}
remove-all-packet-headers
add-packet-header IP TCP
......
set ns [new Simulator]
\end{program}
IMPORTANT: You MUST never remove common header from your
simulation.
As you can see in \nsf{tcl/lib/ns-packet.tcl}, this is enforced
by these header manipulation procs.
{\em Notice that by default, all packet headers are included}.
\section{Packet Classes}
\label{sec:packetclasses}
There are four C++ classes relevant to the handling of packets
and packet headers in general: \code{Packet}, \code{p_info}
\code{PacketHeader}, and \code{PacketHeaderManager}.
The \clsref{Packet}{../ns-2/packet.h}
defines the type for all packets in the simulation;
it is a subclass of \code{Event} so that packets may
be scheduled (e.g.~for later arrival at some queue).
The \clsref{packet\_info}{../ns-2/packet.h} holds all text
representations for packet names.
The \clsref{PacketHeader}{../ns-2/packet.h} provides a base class for
any packet header configured into the simulation.
It essentially provides
enough internal state to locate any particular packet
header in the collection of packet headers present in any given packet.
The \clsref{PacketHeaderManager}{../ns-2/packet.h}
defines a class used to collect and manage currently-configured headers.
It is invoked by a method available to OTcl at simulation configuration
time to enable some subset of the compiled-in packet headers.
\subsection{The Packet Class}
\label{sec:packetclass}
The class Packet defines the structure of a
packet and provides member functions to handle a
free list for objects of this type.
It is illustrated in Figure~\ref{pic:packet} and
defined as follows in \code{packet.h}:
\begin{figure}[ht]
\centerline{\includegraphics{packet}}
\caption{A Packet Object}
\label{pic:packet}
\end{figure}
\begin{program}
class Packet : public Event \{
private:
friend class PacketQueue;
u_char* bits_;
u_char* data_; \* variable size buffer for 'data' */
u_int datalen_; \* length of variable size buffer */
protected:
static Packet* free_;
public:
Packet* next_; \* for queues and the free list */
static int hdrlen_;
Packet() : bits_(0), datalen_(0), next_(0) \{\}
u_char* const bits() \{ return (bits_); \}
Packet* copy() const;
static Packet* alloc();
static Packet* alloc(int);
inline void allocdata(int);
static void free(Packet*);
inline u_char* access(int off) \{
if (off < 0)
abort();
return (&bits_[off]);
\}
inline u_char* accessdata() \{ return data_; \}
\};
\end{program}
This class holds a pointer to a generic array of unsigned
characters (commonly called the ``bag of bits'' or BOB for short)
where packet header fields are stored.
It also holds a pointer to packet ``data'' (which is often not used in
simulations).
The \code{bits_} variable contains the address of
the first byte of the BOB.
Effectively BOB is (currently implemented as) a concatenation
of all the structures defined for each packet header (by convention,
the structures with names beginning \code{hdr_\tup{something}}) that have
been configured in.
BOB generally remains a fixed size throughout a simulation, and
the size is recorded in the \code{Packet::hdrlen_} member
variable.
This size is updated during simulator configuration by
OTcl\footnote{It is not intended to be updated after configuration
time. Doing so {\em should} be possible, but is currently untested.}.
The other methods of the class Packet are for creating new
packets and storing old (unused) ones on a private free list.
Such allocation and deallocation is performed by the
following code (in \nsf{packet.h}):
\begin{program}
inline Packet* Packet::alloc()
\{
Packet* p = free_;
if (p != 0)
free_ = p->next_;
else \{
p = new Packet;
p->bits_ = new u_char[hdrsize_];
if (p == 0 || p->bits_ == 0)
abort();
\}
return (p);
\}
/* {\cf allocate a packet with an n byte data buffer} */
inline Packet* Packet::alloc(int n)
\{
Packet* p = alloc();
if (n > 0)
p->allocdata(n);
return (p);
\}
/* {\cf allocate an n byte data buffer to an existing packet} */
inline void Packet::allocdata(int n)
\{
datalen_ = n;
data_ = new u_char[n];
if (data_ == 0)
abort();
\}
inline void Packet::free(Packet* p)
\{
p->next_ = free_;
free_ = p;
if (p->datalen_) \{
delete p->data_;
p->datalen_ = 0;
\}
\}
inline Packet* Packet::copy() const
\{
Packet* p = alloc();
memcpy(p->bits(), bits_, hdrlen_);
if (datalen_) \{
p->datalen_ = datalen_;
p->data_ = new u_char[datalen_];
memcpy(p->data_, data_, datalen_);
\}
return (p);
\}
\end{program}
The \fcn[]{alloc} method is a support function commonly
used to create new packets.
It is called by \fcn[]{Agent::allocpkt} method on
behalf of agents and is thus not normally invoked directly by most objects.
It first attempts to locate an old packet on the free list and
if this fails allocates a new one using the C++ \code{new} operator.
Note that \code{Packet} class objects and BOBs are
allocated separately.
The \fcn[]{free} method frees a packet by returning it to the free
list.
Note that \emph{packets are never returned to the system's memory allocator}.
Instead, they are stored on a free list when \fcn[]{Packet::free} is called.
The \fcn[]{copy} member creates a new, identical copy of a packet
with the exception of the \code{uid_} field, which is unique.
This function is used by \code{Replicator} objects to support
multicast distribution and LANs.
\subsection{p\_info Class}
\label{sec:pinfoclass}
This class is used as a ``glue'' to bind numeric packet type values
with their symbolic names. When a new packet type is defined, its
numeric code should be added to the enumeration \code{packet_t} (see
\nsf{packet.h}) \footnote{Note: \code{PT\_NTYPE} should remain the last element of this
enumeration.} and its symbolic name should be added to the constructor
of \code{p_info}:
\begin{program}
enum packet_t \{
PT_TCP,
...
PT_NTYPE // This MUST be the LAST one
\};
class p_info \{
public:
p_info() \{
name_[PT_TCP]= "tcp";
...
\}
\}
\end{program}
\subsection{The hdr\_cmn Class}
\label{sec:commonhdr}
Every packet in the simulator has a ``common''
header which is defined in \nsf{packet.h} as follows:
\begin{program}
struct hdr_cmn \{
double ts_; \* timestamp: for q-delay measurement */
packet_t ptype_; \* packet type (see above) */
int uid_; \* unique id */
int size_; \* simulated packet size */
int iface_; \* receiving interface (label) */
/* {\cf Packet header access functions} */
static int offset_;
inline static int& offset() \{ return offset_; \}
inline static hdr_cmn* access(Packet* p) \{
return (hdr_cmn*) p->access(offset_);
\}
/* {\cf per-field member functions} */
int& ptype() \{ return (ptype_); \}
int& uid() \{ return (uid_); \}
int& size() \{ return (size_); \}
int& iface() \{ return (iface_); \}
double& timestamp() \{ return (ts_); \}
\};
\end{program}
This structure primarily defines fields used for tracing
the flow of packets or measuring other quantities.
The time stamp field is used to measure queuing delay
at switch nodes.
The \code{ptype_} field is used to identify the
type of packets, which makes reading traces simpler.
The \code{uid_} field is used by the scheduler in scheduling
packet arrivals.
The \code{size_} field is of general use and gives the
simulated packet's size in bytes.
Note that the actual number of bytes consumed in the simulation
may not relate to the value of this field
(i.e., \code{size_} has \emph{no} relationship
to \code{sizeof(struct hdr_cmn)} or other ns structures).
Rather, it is used most often in computing the time required for a packet
to be delivered along a network link.
As such it should be set to the sum of the
application data size
and IP-, transport-, and application-level headers
for the simulated packet.
The \code{iface_} field is used by the simulator when performing
multicast distribution tree computations.
It is a label indicating (typically) on which link a packet was received.
\subsection{The PacketHeaderManager Class}
\label{sec:packethdrmgr}
An object of the \clsref{PacketHeaderManager}{../ns-2/packet.h} is used
to manage the set of currently-active packet header types and
assign each of them unique offsets in the BOB.
It is defined in both the C++ and OTcl code:
\begin{program}
{\rm From tcl/lib/ns-packet.tcl:}
PacketHeaderManager set hdrlen_ 0
......
foreach prot \{
AODV
ARP
aSRM
Common
CtrMcast
Diffusion
......
TORA
UMP
\} \{
add-packet-header $prot
\}
Simulator instproc create_packetformat \{\} \{
PacketHeaderManager instvar tab_
set pm [new PacketHeaderManager]
foreach cl [PacketHeader info subclass] \{
if [info exists tab_($cl)] \{
set off [$pm allochdr $cl]
$cl offset $off
\}
\}
$self set packetManager_ $pm
\}
PacketHeaderManager instproc allochdr cl \{
set size [$cl set hdrlen_]
$self instvar hdrlen_
set NS_ALIGN 8 \; round up to nearest NS_ALIGN bytes, (needed on sparc/solaris);
set incr [expr ($size + ($NS_ALIGN-1)) & ~($NS_ALIGN-1)]
set base $hdrlen_
incr hdrlen_ $incr
return $base
\}
{\rm From packet.cc:}
/* {\cf manages active packet header types} */
class PacketHeaderManager : public TclObject \{
public:
PacketHeaderManager() \{
bind("hdrlen_", &Packet::hdrlen_);
\}
\};
\end{program} %$
The code in \nsf{tcl/lib/ns-packet.tcl} is executed
when the simulator initializes.
Thus, the {\tt foreach} statement is executed before the
simulation begins, and initializes the OTcl class array
\code{tab_} to contain the mapping between class
the name and the names of the currently active packet header classes.
As discussed above (\ref{sec:ppackethdr}), packet headers should
be accessed using \code{hdr_\tup{hdrname}::access()}.
The \proc[]{create\_packetformat} instance procedure is part of the
basic Simulator class and is called one time during simulator
configuration.
It first creates a single \code{PacketHeaderManager} object.
The C++ constructor links the OTcl instance
variable \code{hdrlen_} (of class \code{PacketHeaderManager})
to the C++ variable \code{Packet::hdrlen_} (a static
member of the \code{Packet} class).
This has the effect of setting \code{Packet::hdrlen_} to
zero.
Note that binding across class types in this fashion is
unusual.
\label{sec:configpacket}
After creating the packet manager, the \code{foreach}
loop enables each of the packet headers of interest.
This loop iterates through the list of defined
packet headers of the form
$(h_i, o_i)$ where $h_i$ is the name of the $i$th header
and $o_i$ is the name of the variable containing the
location of the $h_i$ header in BOB.
The placement of headers is performed by the \code{allochdr}
instproc of the \code{PacketHeaderManager} OTcl class.
The procedure keeps a running variable \code{hdrlen_} with
the current length of BOB as new packet headers are enabled.
It also arranges for 8-byte alignment for any newly-enabled packet
header.
This is needed to ensure that when double-world length quantities
are used in packet headers on machines where double-word alignment
is required, access faults are not produced.\footnote{In
some processer architectures, including the
Sparc and HP-PA, double-word access must be performed on a double-word
boundary (i.e. addresses ending in 0 mod 8). Attempting to perform
unaligned accesses result in an abnormal program termination.}.
\section{Commands at a glance}
\label{sec:pktcommand}
Following is a list of packet-header related procedures:
\begin{flushleft}
\code{Simulator::create_packetformat}
This is an internal simulator procedure and is called once during the simulator
configuration to setup a packetHeaderManager object.
\code{PacketHeaderManager::allochdr}
This is another internal procedure of Class PacketHeaderManager that
keeps track
of a variable called \code{hdrlen_} as new packet-headers are
enabled.
It also allows 8-byte allignment for any newly-enabled pkt header.
\code{add-packet-header}
takes a list of arguments, each of which is a packet header name
(without \code{PacketHeader/} prefix). This global proc will tell
simulator to include the specified packet header(s) in your
simulation.
\code{remove-packet-header} operates in the same syntax, but it
removes the specified headers from your simulation; notice that it
does not remove the common header even it is instructed to do so.
\code{remove-all-packet-headers} is a global Tcl proc. It takes no
argument and removes all packet headers, except the common header,
from your simulation. \code{add-all-packet-headers} is its
counterpart.
\end{flushleft}
\endinput
|