packet_format.tex
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:24k
源码类别:

通讯编程

开发平台:

Visual C++

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