diff --git a/AUTHORS b/AUTHORS
index 6cb66345f..87d19449f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,16 +1,17 @@
-Kirill V. Andreev (kirillano@yandex.ru)
+Kirill Andreev (andreev@iitp.ru)
Nicola Baldo (nbaldo@cttc.es)
Mirko Banchi (mk.banchi@gmail.com)
Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
Raj Bhattacharjea (raj.b@gatech.edu)
Timo Bingmann (timo.bingmann@student.kit.edu)
+Pavel Boyko (boyko@iitp.ru)
Gustavo Carneiro (gjc@inescporto.pt, gjcarneiro@gmail.com)
Angelos Chatzipapas (chatzipa@ceid.upatras.gr)
Luis Cortes (cortes@gatech.edu)
Craig Dowell (craigdo@ee.washington.edu)
David Gross (gdavid.devel@gmail.com)
Tom Henderson (tomhend@u.washington.edu)
-Andrey Hippo (ahippo@yandex.ru)
+Andrey Mazo (mazo@iitp.ru)
Sam Jansen (sam.jansen@gmail.com)
Liu Jian (liujatp@gmail.com)
Joe Kopena (tjkopena@cs.drexel.edu)
diff --git a/CHANGES.html b/CHANGES.html
index d2269f231..997b0e95f 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -67,6 +67,7 @@ allows a user to insert a route and have it redistributed like an OSPF
external LSA to the rest of the topology.
+
Athstats
New classes AthstatsWifiTraceSink and AthstatsHelper.
@@ -75,6 +76,7 @@ external LSA to the rest of the topology.
New trace sources exported by WifiRemoteStationManager: MacTxRtsFailed, MacTxDataFailed, MacTxFinalRtsFailed and MacTxFinalDataFailed.
+
IPv6 additions
Add an IPv6 protocol and ICMPv6 capability.
@@ -86,7 +88,28 @@ router solicitation, DAD
routing objects Ipv6Route and Ipv6MulticastRoute
routing protocols Ipv6ListRouting and Ipv6StaticRouting
General multi-interface mesh stack infrastructure (devices/mesh module).
+
IEEE 802.11s (Draft 3.0) model including Peering Management Protocol and HWMP.
+
Forwarding Layer for Meshing (FLAME) protocol.
+
+
+
+
+
802.11 enhancements
+
+
+
10MHz and 5MHz channel width supported by 802.11a model (Ramon Bauza and Kirill Andreev).
+
+
+
+
Changes to existing API:
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 4175cd258..102e2bdb2 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -49,8 +49,15 @@ New user-visible features
c) added AthstatsHelper, which enables the wifi device to produce
periodic reports similar to the ones generated by madwifi's
athstats tool (Nicola Baldo)
-
-
+
+ d) Wireless Mesh Networking models:
+ - General multi-interface mesh stack infrastructure (devices/mesh module).
+ - IEEE 802.11s (Draft 3.0) model including Peering Management Protocol and HWMP.
+ - Forwarding Layer for Meshing (FLAME) protocol.
+
+ e) 802.11 enhancements:
+ - 10MHz and 5MHz channel width supported by 802.11a model (Ramon Bauza and Kirill Andreev)
+
API changes from ns-3.5
-----------------------
API changes for this release are documented in the file CHANGES.html.
diff --git a/bindings/python/wscript b/bindings/python/wscript
index f6b50c1c6..26d475cca 100644
--- a/bindings/python/wscript
+++ b/bindings/python/wscript
@@ -15,7 +15,7 @@ import Build
import Utils
## https://launchpad.net/pybindgen/
-REQUIRED_PYBINDGEN_VERSION = (0, 11, 0, 697)
+REQUIRED_PYBINDGEN_VERSION = (0, 12, 0)
REQUIRED_PYGCCXML_VERSION = (0, 9, 5)
diff --git a/doc/MeshArchitecture.png b/doc/MeshArchitecture.png
new file mode 100644
index 000000000..4be05d980
Binary files /dev/null and b/doc/MeshArchitecture.png differ
diff --git a/doc/manual/callbacks.texi b/doc/manual/callbacks.texi
index e34dea2ab..36dd2293e 100644
--- a/doc/manual/callbacks.texi
+++ b/doc/manual/callbacks.texi
@@ -8,7 +8,9 @@ it, and details on its implementation.
@menu
* Motivation::
+* Background::
* Using the Callback API::
+* Bound Callbacks::
* Callback locations in ns-3::
* Implementation details::
@end menu
@@ -86,33 +88,230 @@ between TCP and IP:
If the simulator has
made assumptions, and hard coded into the code, that IP always talks
to a transport protocol above, the user may be forced to hack the
-system to get the desired interconnections.
+system to get the desired interconnections, This is clearly not an
+optimal way to design a generic simulator.
-An alternative that provides this flexibility is to use a level of
-indirection that is commonly known in programming as a callback.
-A callback function is not invoked explicitly by the caller but is
-rather delegated to another function that receives the callback
-function's address and can call it.
+@node Background
+@section Background
-You may be familiar with function pointers in C or C++; these can
-be used to implement callbacks. For more information on introductory
-callbacks, an online reference is:
-@uref{http://www.inquiry.com/techtips/cpp_pro/10min/10min0300.asp,,Declaring Function Pointers and Implementing Callbacks} and
-@uref{http://en.wikipedia.org/wiki/Callback_(computer_science),,Callback (computer science)-- Wikipedia}.
+The basic mechanism that allows one to address the problem above is known as
+a @emph{callback}. The ultimate goal is to allow one piece of code to call
+a function (or method in C++) without any specific inter-module dependency.
-The callback API in @command{ns-3} is designed to minimize the overall
-coupling between various pieces of of the simulator
-by making each module depend on the callback API
-itself rather than depend on other modules. It acts as a sort of
-third-party to which work is delegated and which forwards this
-work to the proper target module. This callback API, being based
-on C++ templates,
-is type-safe; that is, it performs static type checks to enforce
-proper signature compatibility between callers and callees. It
-is therefore more type-safe to use than traditional function
-pointers, but the syntax may look imposing at first. This section
-is designed to walk you through the callback system so that you
-can be comfortable using it in @command{ns-3}.
+This ultimately means you need some kind of indirection -- you treat the address
+of the called function as a variable. This variable is called a pointer-to-function
+variable. The relationship between function and pointer-to-function pointer is
+really no different that that of object and pointer-to-object.
+
+In C the canonical example of a pointer-to-function is a
+pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter,
+this could be declared like,
+
+@verbatim
+ int (*pfi)(int arg) = 0;
+@end verbatim
+
+What you get from this is a variable named simply ``pfi'' that is initialized
+to the value 0. If you want to initialize this pointer to something meaningful,
+you have to have a function with a matching signature. In this case,
+
+@verbatim
+ int MyFunction (int arg) {}
+@end verbatim
+
+If you have this target, you can initialize the variable to point to your
+function like,
+
+@verbatim
+ pfi = MyFunction;
+@end verbatim
+
+You can then call MyFunction indirectly using the more suggestive form of
+the call,
+
+@verbatim
+ int result = (*pfi) (1234);
+@end verbatim
+
+This is suggestive since it looks like you are dereferencing the function
+pointer just like you would dereference any pointer. Typically, however,
+people take advantage of the fact that the compiler knows what is going on
+and will just use a shorter form,
+
+@verbatim
+ int result = pfi (1234);
+@end verbatim
+
+Notice that the function pointer obeys value semantics, so you can pass it
+around like any other value. Typically, when you use an asynchronous interface
+you will pass some entity like this to a function which will perform an action
+and ``call back'' to let you know it completed. It calls back by following the
+indirection and executing the provided function.
+
+In C++ you have the added complexity of objects. The analogy with the PFI
+above means you have a pointer to a member function returning an int (PMI)
+instead of the pointer to function returning an int (PFI).
+
+The declaration of the variable providing the indirection looks only slightly
+different,
+
+@verbatim
+ int (MyClass::*pmi) (int arg) = 0;
+@end verbatim
+
+This declares a variable named ``pmi'' just as the previous example declared a
+variable named ``pfi.'' Since the will be to call a method of an instance of
+a particular class, one must declare that method in a class.
+
+@verbatim
+ class MyClass {
+ public:
+ int MyMethod (int arg);
+ };
+@end verbatim
+
+Given this class declaration, one would then initialize that variable like this,
+
+@verbatim
+ pmi = &MyClass::MyMethod;
+@end verbatim
+
+This assigns the address of the code implementing the method to the variable,
+completing the indirection. In order to call a method, the code needs a ``this''
+pointer. This, in turn, means there must be an object of MyClass to refer to.
+A simplistic example of this is just calling a method indirectly (think virtual
+function).
+
+@verbatim
+ int (MyClass::*pmi) (int arg) = 0; // Declare a PMI
+ pmi = &MyClass::MyMethod; // Point at the implementation code
+
+ MyClass myClass; // Need an instance of the class
+ (myClass.*pmi) (1234); // Call the method with an object ptr
+@end verbatim
+
+Just like in the C example, you can use this in an asynchronous call to another
+module which will ``call back'' using a method and an object pointer. The
+straightforward extension one might consider is to pass a pointer to the object
+and the PMI variable. The module would just do,
+
+@verbatim
+ (*objectPtr.*pmi) (1234);
+@end verbatim
+
+to execute the callback on the desired object.
+
+One might ask at this time, ``what's the point''? The called module will have
+to understand the concrete type of the calling object in order to properly make
+the callback. Why not just accept this, pass the correctly typed object pointer
+and do object->Method(1234) in the code instead of the callback? This is
+precisely the problem described above. What is needed is a way to decouple the
+calling function from the called class completely. This requirement led to the
+development of the @emph{Functor}.
+
+A functor is the outgrowth of something invented in the 1960s called a closure.
+It is basically just a packaged-up function call, possibly with some state.
+
+A functor has two parts, a specific part and a generic part, related through
+inheritance. The calling code (the code that executes the callback) will execute
+a generic overloaded @code{operator ()} of a generic functor to cause the callback
+to be called. The called code (the code that wants to be called back) will have
+to provide a specialized implementation of the @code{operator ()} that performs the
+class-specific work that caused the close-coupling problem above.
+
+With the specific functor and its overloaded @code{operator ()} created, the called
+code then gives the specialized code to the module that will execute the callback
+(the calling code).
+
+The calling code will take a generic functor as a parameter, so an implicit cast
+is done in the function call to convert the specific functor to a generic functor.
+This means that the calling module just needs to understand the generic functor
+type. It is decoupled from the calling code completely.
+
+The information one needs to make a specific functor is the object pointer and
+the pointer-to-method address.
+
+The essence of what needs to happen is that the system declares a generic part
+of the functor,
+
+@verbatim
+ template
+ class Functor
+ {
+ public:
+ virtual void operator() (T arg) = 0;
+ };
+@end verbatim
+
+The caller defines a specific part of the functor that really is just there to
+implement the specific operator() method,
+
+@verbatim
+ template
+ class SpecificFunctor : public Functor
+ {
+ public:
+ SpecificFunctor(T* p, int (T::*_pmi)(ARG arg))
+ {
+ m_p = p;
+ m_pmi = pmi;
+ }
+
+ virtual int operator() (ARG arg)
+ {
+ (*m_p.*m_pmi)(arg);
+ }
+ private:
+ void (T::*m_pmi)(ARG arg);
+ T* m_p;
+ };
+@end verbatim
+
+Notice that there are two variables defined in the class above. The m_p
+variable is the object pointer and m_pmi is the variable containing the
+address of the function to execute.
+
+Notice that when @code{operator()} is called, it in turn calls the method provided
+with the object pointer using the C++ PMI syntax.
+
+To use this, one could then declare some model code that takes a generic functor
+as a parameter
+
+@verbatim
+ void LibraryFunction (Functor functor);
+@end verbatim
+
+The code that will talk to the model would build a specific functor and pass it to
+@code{LibraryFunction},
+
+@verbatim
+ MyClass myClass;
+ SpecificFunctor functor (&myclass, MyClass::MyMethod);
+@end verbatim
+
+When @code{LibraryFunction} is done, it executes the callback using the
+@code{operator()} on the generic functor it was passed, and in this particular
+case, provides the integer argument:
+
+@verbatim
+ void
+ LibraryFunction (Functor functor)
+ {
+ // Ececute the library function
+ functor(1234);
+ }
+@end verbatim
+
+Notice that @code{LibraryFunction} is completely decoupled from the specific
+type of the client. The connection is made through the Functor polymorphism.
+
+The Callback API in @command{ns-3} implements object-oriented callbacks using
+the functor mechanism. This callback API, being based on C++ templates, is
+type-safe; that is, it performs static type checks to enforce proper signature
+compatibility between callers and callees. It is therefore more type-safe to
+use than traditional function pointers, but the syntax may look imposing at
+first. This section is designed to walk you through the Callback system so
+that you can be comfortable using it in @command{ns-3}.
@node Using the Callback API
@section Using the Callback API
@@ -132,44 +331,52 @@ This is best observed via walking through an example, based on
@subsection Using the Callback API with static functions
Consider a function:
+
@verbatim
-static double
-CbOne (double a, double b)
-{
- std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
- return a;
-}
+ static double
+ CbOne (double a, double b)
+ {
+ std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
+ return a;
+ }
@end verbatim
Consider also the following main program snippet:
+
@verbatim
-int main (int argc, char *argv[])
-{
- // return type: double
- // first arg type: double
- // second arg type: double
- Callback one;
-}
+ int main (int argc, char *argv[])
+ {
+ // return type: double
+ // first arg type: double
+ // second arg type: double
+ Callback one;
+ }
@end verbatim
-This class template Callback implements what is known as the Functor
-Design Pattern. It is used to declare the type of a callback. It contains
-one mandatory argument (the return type of the function to be assigned
-to this callback) and up to five optional arguments, which each specify
-the type of the arguments (if your function has more than five arguments,
-then this can be handled by extending the callback implementation).
+This is an example of a C-style callback -- one which does not include or need
+a @code{this} pointer. The funtion template @code{Callback} is esentially the
+declaration of the variable containing the pointer-to-function. In the example
+above, we explicitly showed a pointer to a function that returned an integer and
+took a single integer as a parameter, The @code{Callback} template function is
+a generic version of that -- it is used to declare the type of a callback.
-So in the above, we have a declared a callback named "one" that will
-eventually hold a function pointer. The function that it will hold
-must return double and must support two double arguments. If one
-tries to pass a function whose signature does not match the declared
-callback, the compilation will fail.
+The @code{Callback} template requires one mandatory argument (the return type
+of the function to be assigned to this callback) and up to five optional
+arguments, which each specify the type of the arguments (if your particular
+callback function has more than five arguments, then this can be easily handled
+by extending the callback implementation).
+
+So in the above example, we have a declared a callback named "one" that will
+eventually hold a function pointer. The signature of the function that it will
+hold must return double and must support two double arguments. If one tries
+to pass a function whose signature does not match the declared callback, the
+compilation will fail.
+
+Now, we need to tie together this callback instance and the actual target function
+(CbOne). Notice above that CbOne has the same function signature types as the
+callback-- this is important. We can pass in any such properly-typed function
+to this callback. Let's look at this more closely:
-Now, we need to tie together this callback instance and the actual
-target function (CbOne). Notice above that CbOne has the same function
-signature types as the callback-- this is important. We can
-pass in any such properly-typed function to this callback. Let's
-look at this more closely:
@verbatim
static double CbOne (double a, double b) {}
^ ^ ^
@@ -177,109 +384,199 @@ static double CbOne (double a, double b) {}
| | |
Callback one;
@end verbatim
-You can only bind a function to a callback if they have the matching
-signature. The first template argument is the return type, and the
-additional template arguments are the types of the arguments of
-the function signature.
-Now, let's bind our callback "one" to the function that matches its
-signature:
+You can only bind a function to a callback if they have the matching signature.
+The first template argument is the return type, and the additional template
+arguments are the types of the arguments of the function signature.
+
+Now, let's bind our callback "one" to the function that matches its signature:
+
@verbatim
// build callback instance which points to cbOne function
one = MakeCallback (&CbOne);
@end verbatim
-Then, later in the program, if the callback is to be used, it can be
-used as follows:
+This call to @code{MakeCallback} is, in essence, creating one of the specialized
+functors mentioned above. The variable declared using the @code{Callback}
+template function is going to be playing the part of the generic functor. The
+assignment @code{one = MakeCallback (&CbOne)} is the cast that converts the
+specialized functor known to the callee to a generic functor known to the caller.
+
+Then, later in the program, if the callback is needed, it can be used as follows:
@verbatim
-// this is not a null callback
NS_ASSERT (!one.IsNull ());
+
// invoke cbOne function through callback instance
double retOne;
retOne = one (10.0, 20.0);
@end verbatim
-The check @code{IsNull()} ensures that the callback is not null; that there
-is a function to call behind this callback. Then, @code{one()} returns the
-same result as if @code{CbOne()} had been called directly.
-
+The check for @code{IsNull()} ensures that the callback is not null -- that there
+is a function to call behind this callback. Then, @code{one()} executes the
+generic @code{operator()} which is really overloaded with a specific implementation
+of @code{operator()} and returns the same result as if @code{CbOne()} had been
+called directly.
@subsection Using the Callback API with member functions
-Generally, you will not be calling static functions but instead
-public member functions of an object. In this case, an extra
-argument is needed to the MakeCallback function, to tell the system
-on which object the function should be invoked. Consider this example,
-also from main-callback.cc:
+Generally, you will not be calling static functions but instead public member
+functions of an object. In this case, an extra argument is needed to the
+MakeCallback function, to tell the system on which object the function should be
+invoked. Consider this example, also from main-callback.cc:
@verbatim
-class MyCb {
-public:
- int CbTwo (double a) {
- std::cout << "invoke cbTwo a=" << a << std::endl;
- return -5;
- }
-};
+ class MyCb {
+ public:
+ int CbTwo (double a) {
+ std::cout << "invoke cbTwo a=" << a << std::endl;
+ return -5;
+ }
+ };
-int main ()
-{
- ...
- // return type: int
- // first arg type: double
- Callback two;
- MyCb cb;
- // build callback instance which points to MyCb::cbTwo
- two = MakeCallback (&MyCb::CbTwo, &cb);
- ...
-}
+ int main ()
+ {
+ ...
+ // return type: int
+ // first arg type: double
+ Callback two;
+ MyCb cb;
+ // build callback instance which points to MyCb::cbTwo
+ two = MakeCallback (&MyCb::CbTwo, &cb);
+ ...
+ }
@end verbatim
-Here, we pass a (raw) pointer to the @code{MakeCallback<>} function,
-that says, when @code{two ()} is invoked, to call the @code{CbTwo} function
-on the object pointed to by @code{&cb}.
-
-A variation of this is used when objects are referred to by ns-3 smart
-pointers. The MakeCallback API takes a raw pointer, so we need to
-call @code{PeekPointer ()} to obtain this raw pointer. So the example
-above would look like:
+Here, we pass an additional object pointer to the @code{MakeCallback<>} function.
+Recall from the example above that @code{Operator()} will use the pointer to
+member syntax when it executes on an object:
@verbatim
-class MyCb : public Object {
-public:
- int CbTwo (double a) {
- std::cout << "invoke cbTwo a=" << a << std::endl;
- return -5;
- }
-};
-
-int main ()
-{
- ...
- // return type: int
- // first arg type: double
- Callback two;
- Ptr cb = CreateObject ();
- // build callback instance which points to MyCb::cbTwo
- two = MakeCallback (&MyCb::CbTwo, PeekPointer (cb));
- ...
-}
+ virtual int operator() (ARG arg)
+ {
+ (*m_p.*m_pmi)(arg);
+ }
@end verbatim
+And so we needed to provide the two variables (@code{m_p} and @code{m_pmi}) when
+we made the specific functor. The line,
+
+@verbatim
+ two = MakeCallback (&MyCb::CbTwo, &cb);
+@end verbatim
+
+does precisely that. In this case,
+
+When @code{two ()} is invoked,
+
+@verbatim
+ int result = two (1.0);
+@end verbatim
+
+I will result in a call the @code{CbTwo} member function (method) on the object
+pointed to by @code{&cb}.
+
@subsection Building Null Callbacks
It is possible for callbacks to be null; hence it may be wise to
check before using them. There is a special construct for a null
callback, which is preferable to simply passing "0" as an argument;
it is the @code{MakeNullCallback<>} construct:
+
@verbatim
two = MakeNullCallback ();
- // invoking a null callback is just like
- // invoking a null function pointer:
- // it will crash at runtime.
- //int retTwoNull = two (20.0);
NS_ASSERT (two.IsNull ());
@end verbatim
+Invoking a null callback is just like invoking a null function pointer: it will
+crash at runtime.
+
+@node Bound Callbacks
+@section Bound Callbacks
+
+A very useful extension to the functor concept is that of a Bound Callback.
+Previously it was mentioned that closures were originally function calls
+packaged up for later execution. Notice that in all of the Callback
+descriptions above, there is no way to package up any parameters for use
+later -- when the @code{Callback} is called via @code{operator()}. All of
+the parameters are provided by the calling function.
+
+What if it is desired to allow the client function (the one that provides the
+callback) to provide some of the parameters? Alexandrescu calls the process of
+allowing a client to specify one of the parameters @emph{binding}. One of the
+parameters of @code{operator()} has been bound (fixed) by the client.
+
+Some of our pcap tracing code provides a nice example of this. There is a
+function that needs to be called whenever a packet is received. This function
+calls an object that actually writes the packet to disk in the pcap file
+format. The signature of one of these functions will be,
+
+@verbatim
+ static void SniffEvent (Ptr writer, Ptr packet);
+@end verbatim
+
+The static keyword means this is a static function which does not need a
+@code{this} pointer, so it will be using C-style callbacks. We don't want the
+calling code to have to know about anything but the Packet. What we want there
+is just a call that looks like,
+
+@verbatim
+ m_promiscSnifferTrace (m_currentPkt);
+@end verbatim
+
+What we want to do is to @emph{bind} the @code{Ptr writer} to the
+specific callback implementation when it is created and arrange for the
+@code{operator()} of the Callback to provide that parameter for free.
+
+We provide the @code{MakeBoundCallback} template function for that purpose. It
+takes the same parameters as the @code{MakeCallback} template function but also
+takes the parameters to be bound. In the case of the example above,
+
+@verbatim
+ MakeBoundCallback (&CsmaHelper::SniffEvent, pcap));
+@end verbatim
+
+Will create a specific callback implementation that knows to add in the extra
+bound arguments. Conceptually, it extends the specific functor described above
+with one or more bound arguments
+
+@verbatim
+ template
+ class SpecificFunctor : public Functor
+ {
+ public:
+ SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
+ {
+ m_p = p;
+ m_pmi = pmi;
+ m_boundArg = boundArg;
+ }
+
+ virtual int operator() (ARG arg)
+ {
+ (*m_p.*m_pmi)(m_boundArg, arg);
+ }
+ private:
+ void (T::*m_pmi)(ARG arg);
+ T* m_p;
+ BOUND_ARG m_boundArg;
+ };
+@end verbatim
+
+You can see that when the specific functor is created, the bound argument is saved
+in the functor / callback object itself. When the @code{operator()} is invoked with
+the single parameter, as in
+
+@verbatim
+ m_promiscSnifferTrace (m_currentPkt);
+@end verbatim
+
+the imlpementation of @code{operator()} adds the bound parameter into the actual
+function call:
+
+@verbatim
+ (*m_p.*m_pmi)(m_boundArg, arg);
+@end verbatim
+
@node Callback locations in ns-3
@section Callback locations in @command{ns-3}
@@ -290,18 +587,18 @@ more visible ones to typical users:
@subsection Layer-2/Layer-3 API
@subsection Tracing subsystem
@subsection Routing
-Route Reply
@node Implementation details
@section Implementation details
-This section is advanced explanation for C++ experts interested in
-the implementation, and may be skipped by most users.
+The code snippets above are simplistic and only designed to illustrate the mechanism
+itself. The actual Callback code is quite complicated and very template-intense and
+a deep understanding of the code is not required. If interested, expert users may
+find the following useful:
-This code was originally written based on the techniques described
+The code was originally written based on the techniques described
@uref{http://www.codeproject.com/cpp/TTLFunction.asp,,here}.
-It was subsequently rewritten to follow the architecture
-outlined in
+It was subsequently rewritten to follow the architecture outlined in
@uref{http://www.amazon.com/Modern-C\%2B\%2B-Design-Programming-Patterns/dp/0201704315/ref=pd_bbs_sr_1/102-0157303-1900156?ie=UTF8\&s=books\&qid=1187982662\&sr=1-1,,Modern C++ Design: Generic Programming and Design Patterns Applied-- Alexandrescu}, chapter 5, "Generalized Functors".
This code uses:
@@ -319,9 +616,7 @@ member functions.
value semantics.
@end itemize
-This code most notably departs from the Alexandrescu
-implementation in that it does not use type lists to specify
-and pass around the types of the callback arguments.
-Of course, it also does not use copy-destruction semantics
-and relies on a reference list rather than autoPtr to hold
-the pointer.
+This code most notably departs from the Alexandrescu implementation in that it
+does not use type lists to specify and pass around the types of the callback
+arguments. Of course, it also does not use copy-destruction semantics and
+relies on a reference list rather than autoPtr to hold the pointer.
diff --git a/doc/manual/manual.texi b/doc/manual/manual.texi
index ba1a18a59..6899029b3 100644
--- a/doc/manual/manual.texi
+++ b/doc/manual/manual.texi
@@ -82,8 +82,9 @@ see @uref{http://www.nsnam.org/docs/manual.pdf}.
@menu
* Random variables::
* Callbacks::
-* Attributes::
* Object model::
+* Attributes::
+* Tracing::
* RealTime::
* Emulation::
* Packets::
@@ -100,8 +101,9 @@ see @uref{http://www.nsnam.org/docs/manual.pdf}.
@include random.texi
@include callbacks.texi
-@include attributes.texi
@include objects.texi
+@include attributes.texi
+@include tracing.texi
@include realtime.texi
@include emulation.texi
@include packets.texi
diff --git a/doc/manual/tracing.texi b/doc/manual/tracing.texi
new file mode 100644
index 000000000..d445dfc12
--- /dev/null
+++ b/doc/manual/tracing.texi
@@ -0,0 +1,325 @@
+@node Tracing
+@chapter Tracing
+
+The tracing subsystem is one of the most important mechansisms to understand in
+@command{ns-3}. In most cases, @command{ns-3} users will have a brilliant idea
+for some new and improved networking feature. In order to verify that this
+idea works, the researcher will make changes to an existing system and then run
+experiments to see how the new feature behaves by gathering some form of statistic
+that captures the behavior of the feature.
+
+In other words, the whole point of running a simulation is to generate output for
+further study. In @command{ns-3}, the subsystem that enables a researcher to do
+this is the tracing subsystem.
+
+@menu
+* Motivation::
+* Overview::
+* Using the Tracing API::
+* Implementation details::
+@end menu
+
+@node Motivation
+@section Motivation
+
+There are many ways to get information out of a program. The most straightforward
+way is to just directly print the information to the standard output, as in,
+
+@verbatim
+ #include
+ ...
+ int main ()
+ {
+ ...
+ std::cout << ``The value of x is `` << x << std::endl;
+ ...
+ }
+@end verbatim
+
+This is workable in small environments, but as your simulations get more and more
+compliated, you end up with more and more prints and the task of parsing and
+performing computations on the output begins to get harder and harder.
+
+Another thing to consider is that every time a new tidbit is needed, the software
+core must be edited and another print introduced. There is no standardized way
+to control all of this output, so the amount of output tends to grow without
+bounds. Eventually, the bandwidth required for simply outputting this information
+begins to limit the running time of the simulation. The output files grow to
+enormous sizes and parsing them becomes a problem.
+
+@command{ns-3} provides a simple mechanism for logging and providing some control
+over output via @emph{Log Components}, but the level of control is not very fine
+grained at all. The logging module is a relatively blunt instrument.
+
+It is desirable to have a facility that allows one to reach into the core system
+and only get the information required without having to change and recompile the
+core system. Even better would be a system that notified the user when an item
+of interest changed or an interesting event happened.
+
+The @command{ns-3} tracing system is designed to work along those lines and is
+well-integrated with the Attribute and Config substems allowing for relatively
+simple use scenarios.
+
+@node Overview
+@section Overview
+
+The tracing subsystem relies heavily on the @code{ns-3} Callback and Attribute
+mechanisms. You should read and understand the corresponding sections of the
+manual before attempting to understand the tracing system.
+
+The ns-3 tracing system is built on the concepts of independent tracing sources
+and tracing sinks; along with a uniform mechanism for connecting sources to sinks.
+
+Trace sources are entities that can signal events that happen in a simulation and
+provide access to interesting underlying data. For example, a trace source could
+indicate when a packet is received by a net device and provide access to the
+packet contents for interested trace sinks. A trace source might also indicate
+when an iteresting state change happens in a model. For example, the congestion
+window of a TCP model is a prime candidate for a trace source.
+
+Trace sources are not useful by themselves; they must be connected to other pieces
+of code that actually do something useful with the information provided by the source.
+The entities that consume trace information are called trace sinks. Trace sources
+are generators of events and trace sinks are consumers.
+
+This explicit division allows for large numbers of trace sources to be scattered
+around the system in places which model authors believe might be useful. Unless
+a user connects a trace sink to one of these sources, nothing is output. This
+arrangement allows relatively unsophisticated users to attach new types of sinks
+to existing tracing sources, without requiring editing and recompiling the core
+or models of the simulator.
+
+There can be zero or more consumers of trace events generated by a trace source.
+One can think of a trace source as a kind of point-to-multipoint information link.
+
+The ``transport protocol'' for this conceptual point-to-multipoint link as an
+@code{ns-3} @code{Callback}.
+
+Recall from the Callback Section that callback facility is a way to allow two
+modules in the system to communicate via function calls while at the same time
+decoupling the calling function from the called class completely. This is the
+same requirement as outlined above for the tracing system.
+
+Basically, a trace source @emph{is} a callback. When a trace sink expresses
+interest in receiving trace events, it adds a callback to a list of callbacks
+held by the trace source. When an interesting event happens, the trace source
+invokes its @code{operator()} providing zero or more parameters. This tells
+the source to go through its list of callbacks invoking each one in turn. In
+this way, the parameter(s) are communicated to the trace sinks, which are just
+functions.
+
+@subsection The Simplest Example
+
+It will be useful to go walk a quick example just to reinforce what we've said.
+
+@verbose
+ #include ``ns3/object.h''
+ #include ``ns3/uinteger.h''
+ #include ``ns3/traced-value.h''
+ #include ``ns3/trace-source-accessor.h''
+
+ #include
+
+ using namespace ns3;
+@end verbose
+
+The first thing to do is include the required files. As mentioned above, the
+trace system makes heavy use of the Object and Attribute systems. The first
+two includes bring in the declarations for those systems. The file,
+@code{traced-value.h} brings in the required declarations for tracing data
+that obeys value semantics.
+
+In general, value semantics just means that you can pass the object around,
+not an address. In order to use value semantics at all you have to have an
+object with an associated copy constructor and assignment operator
+available. We extend the requirements to talk about the set of operators
+that are pre-defined for plain-old-data (POD) types. Operator=, operator++,
+operator--, operator+, operator==, etc.
+
+What this all means is that you will be able to trace changes to an object
+made using those operators.
+
+@verbatim
+ class MyObject : public Object
+ {
+ public:
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("MyObject")
+ .SetParent (Object::GetTypeId ())
+ .AddConstructor ()
+ .AddTraceSource ("MyInteger",
+ "An integer value to trace.",
+ MakeTraceSourceAccessor (&MyObject::m_myInt))
+ ;
+ return tid;
+ }
+
+ MyObject () {}
+ TracedValue m_myInt;
+ };
+@end verbatim
+
+Since the tracing system is integrated with Attributes, and Attributes work
+with Objects, there must be an @command{ns-3} @code{Object} for the trace source
+to live in. The two important lines of code are the @code{.AddTraceSource} and
+the @code{TracedValue} declaration.
+
+The @code{.AddTraceSource} provides the ``hooks'' used for connecting the trace
+source to the outside world. The @code{TracedValue} declaration provides the
+infrastructure that overloads the operators above and drives the callback
+process.
+
+@verbatim
+ void
+ IntTrace (Int oldValue, Int newValue)
+ {
+ std::cout << ``Traced `` << oldValue << `` to `` << newValue << std::endl;
+ }
+@end verbatim
+
+This is the definition of the trace sink. It corresponds directly to a callback
+function. This function will be called whenever one of the operators of the
+@code{TracedValue} is executed.
+
+@verbatim
+ int
+ main (int argc, char *argv[])
+ {
+ Ptr myObject = CreateObject ();
+
+ myObject->TraceConnectWithoutContext ("MyInt", MakeCallback(&IntTrace));
+
+ myObject->m_myInt = 1234;
+ }
+@end verbatim
+
+In this snippet, the first thing that needs to be done is to create the object
+in which the trace source lives.
+
+The next step, the @code{TraceConnectWithoutContext}, forms the connection
+between the trace source and the trace sink. Notice the @code{MakeCallback}
+template function. Recall from the Callback section that this creates the
+specialized functor responsible for providing the overloaded @code{operator()}
+used to ``fire'' the callback. The @code{TraceConnectWithoutContext}, takes
+a string parameter that provides the name of the Attribute assigned to the
+trace source. Let's ignore the bit about context for now.
+
+Finally, the line,
+
+@verbatim
+ myObject->m_myInt = 1234;
+@end verbatim
+
+should be interpreted as an invocation of @code{operator=} on the member
+variable @code{m_myInt} with the integer @code{1234} passed as a parameter.
+It turns out that this operator is defined (by @code{TracedValue}) to execute
+a callback that returns void and takes two integer values as parameters --
+an old value and a new value for the integer in question. That is exactly
+the function signature for the callback function we provided -- @code{IntTrace}.
+
+To summarize, a trace source is, in essence, a variable that holds a list of
+callbacks. A trace sink is a function used as the target of a callback. The
+Attribute and object type information systems are used to provide a way to
+connect trace sources to trace sinks. The act of ``hitting'' a trace source
+is executing an operator on the trace source which fires callbacks. This
+results in the trace sink callbacks registering interest in the source being
+called with the parameters provided by the source.
+
+@subsection Using the Config Subsystem to Connect to Trace Sources
+
+The @code{TraceConnectWithoutContext} call shown above in the simple example is
+actually very rarely used in the system. More typically, the @code{Config}
+subsystem is used to allow selecting a trace source in the system using what is
+called a @emph{config path}.
+
+For example, one might find something that looks like the following in the system
+(taken from @code{examples/tcp-large-transfer.cc})
+
+@verbatim
+ void CwndTracer (uint32_t oldval, uint32_t newval) {}
+
+ ...
+
+ Config::ConnectWithoutContext (
+ "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
+ MakeCallback (&CwndTracer));
+@end verbatim
+
+This should look very familiar. It is the same thing as the previous example,
+except that a static member function of @code{Config} is being called instead of
+a method on @code{Object}, and instead of an @code{Attribute} name, a path is
+being provided.
+
+The first thing to do is to read the path backward. The last segment of the path
+must be an @code{Attribute} of an @code{Object}. In fact, if you had a pointer to
+the @code{Object} that has the @code{Attribute} handy, you could write this just like
+the previous example:
+
+@verbatim
+ void CwndTracer (uint32_t oldval, uint32_t newval) {}
+
+ ...
+
+ object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
+@end verbatim
+
+And it turns out that @code{Config::ConnectWithoutContext} does exactly that. This
+function takes a path that represents a chain of @code{Object} pointers and follows
+them until it gets to the end of the path and interprets the last segment as an
+@code{Attribute} on the last object.
+
+The leading ``/'' character in the path refers to a so-called namespace. One of the
+predefined namespaces in the config system is ``NodeList'' which is a list of all of
+the nodes in the simulation. Items in the list are referred to by indices into the
+list, so ``/NodeList/0'' refers to the zeroth node in the list of nodes created by
+the simulation. This node is actually a @code{Ptr} and so is a subclass of
+an @code{ns3::Object}.
+
+As described in the Object Model section, @code{ns-3} supports an object aggregation
+model. The next path segment begins with the ``$'' character which indicates a
+@code{GetObject} call should be made looking for the type that follows. When a
+node is initialized by an @code{InternetStackHelper} a number of interfaces are
+aggregated to the node. One of these is the TCP level four protocol. The runtime
+type of this protocol object is ``ns3::TcpL4Protocol''. When the @code{GetObject}
+is executed, it returns a pointer to the object of this type.
+
+The @code{TcpL4Protocol} class defines an Attribute called ``SocketList'' which is
+a list of sockets. Each socket is actually an @code{ns3::Object} with its own
+@code{Attributes}. The items in the list of sockets are referred to by index just
+as in the NodeList, so ``SocketList/0'' refers to the zeroth socket in the list
+of sockets on the zeroth node in the NodeList -- the first node constructed in the
+simulation.
+
+This socket, the type of which turns out to be an @code{ns3::TcpSocketImpl} defines
+an attribute called ``CongestionWindow'' which is a @code{TracedValue}.
+The @code{Config::ConnectWithoutContext} now does a,
+
+@verbatim
+ object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
+@end verbatim
+
+using the object pointer from ``SocketList/0'' which makes the connection between
+the trace source defined in the socket to the callback -- @code{CwndTracer}.
+
+Now, whenever a change is made to the @code{TracedValue} representing the
+congestion window in the TCP socket, the registered callback will be executed and
+the function @code{Cwndtracer} will be called printing out the old and new values
+of the TCP congestion window.
+
+@node Using the Tracing API
+@section Using the Tracing API
+
+ There
+are three levels of interaction with the tracing system:
+
+@itemize @bullet
+@item Beginning user can easily control which objects are participating in tracing;
+@item Intermediate users can extend the tracing system to modify the output format
+generated or use existing trace sources in different ways, without modifying the
+core of the simulator;
+@item Advanced users can modify the simulator core to add new tracing sources and
+sinks.
+
+@node Implementation details
+@section Implementation details
diff --git a/doc/testing/Makefile b/doc/testing/Makefile
new file mode 100644
index 000000000..ce641f161
--- /dev/null
+++ b/doc/testing/Makefile
@@ -0,0 +1,44 @@
+TEXI2HTML = texi2html
+TEXI2PDF = texi2dvi --pdf
+EPSTOPDF = epstopdf
+DIA = dia
+CONVERT = convert
+CSS = --css-include=testing.css
+SPLIT = --split section
+
+FIGURES = figures
+VPATH = $(FIGURES)
+
+IMAGES_EPS = \
+
+IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
+IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
+
+IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
+
+CHAPTERS = \
+ testing.texi \
+ overview.texi \
+ propagation-loss.texi \
+
+%.eps : %.dia; $(DIA) -t eps $< -e $@
+%.png : %.dia; $(DIA) -t png $< -e $@
+%.pdf : %.eps; $(EPSTOPDF) $< -o=$@
+
+all: $(IMAGES) testing.pdf testing.html testing/testing.html
+
+testing.pdf: $(IMAGES) $(CHAPTERS)
+ $(TEXI2PDF) testing.texi
+
+testing.html: $(IMAGES) $(CHAPTERS)
+ $(TEXI2HTML) ${CSS} testing.texi
+
+testing/testing.html: $(IMAGES) $(CHAPTERS)
+ $(TEXI2HTML) ${CSS} ${SPLIT} testing.texi
+
+figures-clean:
+ rm -rf $(IMAGES)
+
+clean: figures-clean
+ rm -rf testing.aux testing.cp testing.cps testing.fn testing.ky testing.pg
+ rm -rf testing.tp testing.vr testing.toc testing.log testing.pdf testing.html testing/
diff --git a/doc/testing/background.texi b/doc/testing/background.texi
new file mode 100644
index 000000000..3c58ea2ce
--- /dev/null
+++ b/doc/testing/background.texi
@@ -0,0 +1,215 @@
+@c ========================================================================
+@c Background
+@c ========================================================================
+
+@node Background
+@chapter Background
+
+Writing defect-free software is a difficult proposition. There are many
+dimensions to the problem and there is much confusion regarding what is
+meant by different terms in different contexts. We have found it worthwhile
+to spend a little time reviewing the subject and defining some terms.
+
+Software testing may be loosely defined as the process of executing a program
+with the intent of finding errors. When one enters a discussion regarding
+software testing, it quickly becomes apparent that there are many distinct
+mind-sets with which one can approach the subject.
+
+For example, one could break the process into broad functional categories
+like ``correctness testing,'' ``performance testing,'' ``robustness testing''
+and ``security testing.'' Another way to look at the problem is by life-cycle:
+``requirements testing,'' ``design testing,'' ``acceptance testing,'' and
+``maintenance testing.'' Yet another view is by the scope of the tested system.
+In this case one may speak of ``unit testing,'' ``component testing,''
+``integration testing,'' and ``system testing.'' These terms are also not
+standardized in any way, and so ``maintenance testing'' and ``regression
+testing'' may be heard interchangeably. Additionally, these terms are often
+misused.
+
+There are also a number of different philosophical approaches to software
+testing. For example, some organizations advocate writing test programs
+before actually imlementing the desired software, yielding ``test-driven
+development.'' Some organizations advocate testing from a customer perspective
+as soon as possible, following a parallel with the agile development process:
+``test early and test often.'' This is sometimes called ``agile testing.'' It
+seems that there is at least one approach to testing for every development
+methodology.
+
+The @command{ns-3} project is not in the business of advocating for any one of
+these processes, but the project as a whole has requirements that help inform
+the test process.
+
+Like all major software products, @command{ns-3} has a number of qualities that
+must be present for the product to succeed. From a testing perspective, some
+of these qualities that must be addressed are that @command{ns-3} must be
+``correct,'' ``robust,'' ``performant'' and ``maintainable.'' Ideally there
+should be metrics for each of these dimensions that are checked by the tests
+to identify when the product fails to meed its expectations / requirements.
+
+@node Correctness
+@section Correctness
+
+The essential purpose of testing is to determine that a piece of software
+behaves ``correctly.'' For @command{ns-3} this means that if we simulate
+something, the simulation should faithfully represent some physical entity or
+process to a specified accuracy and precision.
+
+It turns out that there are two perspectives from which one can view
+correctness. Verifying that a particular process is implemented according
+to its specification is generically called verification. The process of
+deciding that the specification is correct is generically called validation.
+
+@node ValidationAndVerification
+@section Validation and Verification
+
+A computer model is a mathematical or logical representation of something. It
+can represent a vehicle, a frog or a networking card. Models can also represent
+processes such as global warming, freeway traffic flow or a specification of a
+networking protocol. Models can be completely faithful representations of a
+logical process specification, but they necessarily can never completely
+simulate a physical object or process. In most cases, a number of
+simplifications are made to the model to make simulation computationally
+tractable.
+
+Every model has a @emph{target system} that it is attempting to simulate. The
+first step in creating a simulation model is to identify this target system and
+the level of detail and accuracy that the simulation is desired to reproduce.
+In the case of a logical process, the target system may be identified as ``TCP
+as defined by RFC 793.'' In this case, it will probably be desirable to create
+a model that completely and faithfully reproduces RFC 793. In the case of a
+physical process this will not be possible. If, for example, you would like to
+simulate a wireless networking card, you may determine that you need, ``an
+accurate MAC-level implementation of the 802.11 specification and [...] a
+not-so-slow PHY-level model of the 802.11a specification.''
+
+Once this is done, one can develop an abstract model of the target system. This
+is typically an exercise in managing the tradeoffs between complexity, resource
+requiremens and accuracy. The process of developing an abstract model has been
+called @emph{model qualification} in the literature. In the case of a TCP
+protocol, this process results in a design for a collection of objects,
+interactions and behaviors that will fully implement RFC 793 in @command{ns-3}.
+In the case of the wireless card, this process results in a number of tradeoffs
+to allow the physical layer to be simulated and the design of a network device
+and channel for ns-3, along with the desired objects, interactions and behaviors.
+
+This abstract model is then developed into an @command{ns-3} model that
+implements the abstract model as a computer program. The process of getting the
+implementation to agree with the abstract model is called @emph{model
+verification} in the literature.
+
+The process so far is open loop. What remains is to make a determination that a
+given ns-3 model has some connection to some reality -- that a model is an
+accurate representation of a real system, whether a logical process or a physical
+entity.
+
+If one is going to use a simulation model to try and predict how some real
+system is going to behave, there must be some reason to believe your results --
+i.e., can one trust that an inference made from the model translates into a
+correct prediction for the real system. The process of getting the ns-3 model
+behavior to agree with the desired target system behavior as defined by the model
+qualification process is called @emph{model validation} in the literature. In the
+case of a TCP implementation, you may want to compare the behavior of your ns-3
+TCP model to some reference implementation in order to validate your model. In
+the case of a wireless physical layer simulation, you may want to compare the
+behavior of your model to that of real hardware in a controlled setting,
+
+The @command{ns-3} testing environment provides tools to allow for both model
+validation and testing, and encourages the publication of validation results.
+
+@node Robustness
+@section Robustness
+
+Robustness is the quality of being able to withstand stresses, or changes in
+environments, inputs or calculations, etc. A system or design is ``robust''
+if it can deal with such changes with minimal loss of functionality.
+
+This kind of testing is usually done with a particular focus. For example, the
+system as a whole can be run on many different system configurations to
+demonstrate that it can perform correctly in a large number of environments.
+
+The system can be also be stressed by operating close to or beyond capacity by
+generating or simulating resource exhaustion of various kinds. This genre of
+testing is called ``stress testing.''
+
+The system and its components may be exposed to so-called ``clean tests'' that
+demostrate a positive result -- that is that the system operates correctly in
+response to a large variation of expected configurations.
+
+The system and its components may also be exposed to ``dirty tests'' which
+provide inputs outside the expected range. For example, if a module expects a
+zero-terminated string representation of an integer, a dirty test might provide
+an unterminated string of random characters to verify that the system does not
+crash as a result of this unexpected input. Unfortunately, detecting such
+``dirty'' input and taking preventive measures to ensure the system does not
+fail catasrophically can require a huge amount of development overhead. In
+order to reduce development time, a decision was taken early on in the project
+to minimize the amount of parameter validation and error handling in the
+@command{ns-3} codebase. For this reason, we do not spend much time on dirty
+testing -- it would just uncover the results of the design decision we know
+we took.
+
+We do want to deonstrate that @command{ns-3} software does work across some set
+of conditions. We borrow a couple of definitions to narrow this down a bit.
+The @emph{domain of applicability} is a set of prescribed conditions for which
+the model has been tested, compared against reality to the extent possible, and
+judged suitable for use. The @emph{range of accuracy} is an agreement between
+the computerized model and reality within a domain of applicability.
+
+The @command{ns-3} testing environment provides tools to allow for setting up
+and running test environments over multiple systems (buildbot) and provides
+classes to encourage clean tests to verify the operation of the system over the
+expected ``domain of applicability'' and ``range of accuraccy.''
+
+@node Performant
+@section Performant
+
+Okay, ``performant'' isn't a real English word. It is, however, a very concise
+neologism that is quite often used to describe what we want @command{ns-3} to
+be: powerful and fast enough to get the job done.
+
+This is really about the broad subject of software performance testing. One of
+the key things that is done is to compare two systems to find which performs
+better (cf benchmarks). This is used to demonstrate that, for example,
+@code{ns-3} can perform a basic kind of simulation at least as fast as a
+competing product, or can be used to identify parts of the system that perform
+badly.
+
+In the @code{ns-3} test framework, we provide support for timing various kinds
+of tests.
+
+@node Maintainability
+@section Maintainability
+
+A software product must be maintainable. This is, again, a very broad
+statement, but a testing framework can help with the task. Once a model has
+been developed, validated and verified, we can repeatedly execute the suite
+of tests for the entire system to ensure that it remains valid and verified
+over its lifetime.
+
+When a feature stops functioning as intended after some kind of change to the
+system is integrated, it is called generically a regression. Originally the
+term regression referred to a change that caused a previously fixed bug to
+reappear, but the term has evolved to describe any kind of change that breaks
+existing functionality. There are many kinds of regressions that may occur
+in practice.
+
+A @emph{local regression} is one in which a change affects the changed component
+directy. For example, if a component is modified to allocate and free memory
+but stale pointers are used, the component itself fails.
+
+A @emph{remote regression} is one in which a change to one component breaks
+functionality in another component. This reflects violation of an implied but
+possibly unrecognized contract between components.
+
+An @emph{unmasked regression} is one that creates a situation where a previously
+existing bug that had no affect is suddenly exposed in the system. This may
+be as simple as exercising a code path for the first time.
+
+A @emph{performance regression} is one that causes the performance requirements
+of the system to be violated. For example, doing some work in a low level
+function that may be repeated large numbers of times may suddenly render the
+system unusable from certain perspectives.
+
+The @command{ns-3} testing framework provides tools for automating the process
+used to validate and verify the code in nightly test suites to help quickly
+identify possible regressions.
diff --git a/doc/testing/how-to-write-tests.texi b/doc/testing/how-to-write-tests.texi
new file mode 100644
index 000000000..16b8cac34
--- /dev/null
+++ b/doc/testing/how-to-write-tests.texi
@@ -0,0 +1,8 @@
+@c ========================================================================
+@c How to write tests
+@c ========================================================================
+
+@node How to write tests
+@chapter How to write tests
+
+To be completed.
diff --git a/doc/testing/overview.texi b/doc/testing/overview.texi
new file mode 100644
index 000000000..0217c08fc
--- /dev/null
+++ b/doc/testing/overview.texi
@@ -0,0 +1,16 @@
+@c ========================================================================
+@c Overview
+@c ========================================================================
+
+@node Overview
+@chapter Overview
+
+This document is concerned with the testing and validation of @command{ns-3}
+software.
+
+This document provides
+@itemize @bullet
+@item a description of the ns-3 testing framework;
+@item a guide to model developers or new model contributors for how to write tests;
+@item validation and verification results reported to date.
+@end itemize
diff --git a/doc/testing/propagation-loss.texi b/doc/testing/propagation-loss.texi
new file mode 100644
index 000000000..5fdf229a3
--- /dev/null
+++ b/doc/testing/propagation-loss.texi
@@ -0,0 +1,121 @@
+@node Propagation Loss Models
+@chapter Propagation Loss Models
+@anchor{chap:propagation-loss-models}
+
+This chapter describes validation of ns-3 propagation loss models.
+
+@section FriisPropagationLossModel
+
+@subsection Model reference
+
+From source: @uref{http://www.scribd.com/doc/6650712/Wireless-CommunicationsPrinciples-and-Practice-Theodore-S,, Wireless Communications-Principles and Practice ,Theodore S Rappaport pg. 71 }
+
+Given equation:
+@verbatim
+Pr = Pt*Gt*Gr*lmb^2/((4*pi)^2*d^2*L)
+
+Pt = 10^(17.0206/10)/10^3 = .05035702
+Pr = .05035702*.125^2/((4*pi)^2*d*1) = 4.98265e-6/d^2
+
+bandwidth = 2.2*10^7
+m_noiseFigure = 5.01187
+noiseFloor = ((Thermal noise (K)* BOLTZMANN * bandwidth)* m_noiseFigure)
+noiseFloor = ((290*1.3803*10^-23*2.2*10^7)*5.01187) = 4.41361e-13W
+no interference, so SNR = Pr/4.41361e-13W
+
+Distance :: Pr :: SNR
+100 4.98265e-10W 1128.93
+500 1.99306e-11W 45.1571
+1000 4.98265e-12W 11.2893
+2000 1.24566e-12W 2.82232
+3000 5.53628e-13W 1.25436
+4000 3.11416e-13W 0.70558
+5000 1.99306e-13W 0.451571
+6000 1.38407e-13W 0.313591
+@end verbatim
+
+@subsection Validation test
+
+Test program available online at: @uref{http://xxx.xxx.com,,}
+
+Taken at default settings (packetSize = 1000, numPackets = 1, lambda = 0.125, 802.11b at 2.4GHz):
+@verbatim
+Distance :: Pr :: SNR
+100 4.98265e-10W 1128.93
+500 1.99306e-11W 45.1571
+1000 4.98265e-12W 11.2893
+2000 1.24566e-12W 2.82232
+3000 5.53628e-13W 1.25436
+4000 3.11416e-13W 0.70558
+5000 1.99306e-13W 0.451571
+6000 1.38407e-13W 0.313591
+7000 1.01687e-13W 0.230393
+8000 7.78539e-14W 0.176395
+@end verbatim
+
+@subsection Discussion
+
+As can be seen, the SNR outputted from the simulator, and the SNR computed from the source's equation are identical.
+
+@section LogDistancePropagationLossModel
+
+@subsection Model reference
+
+From source: @uref{http://www.plextek.co.uk/papers/aps2005mcw.pdf,, Urban Propagation Measurements and Statistical Path Loss Model at 3.5 GHz, Marcus C. Walden, Frank J. Rowsell}
+
+Given equation:
+@verbatim
+PL{dBm} = PL(d0) + 10*n*log(d/d0) + Xs
+
+PL(1) from friis at 2.4GHz: 40.045997dBm
+PL{dBm} = 10*log(.050357/Pr) = 40.045997 + 10*n*log(d) + Xg
+Pr = .050357/(10^((40.045997 + 10*n*log(d) + Xg)/10))
+
+bandwidth = 2.2*10^7
+m_noiseFigure = 5.01187
+no interference, so SNR = Pr/4.41361e-13W
+@end verbatim
+
+taking Xg to be constant at 0 to match ns-3 output:
+@verbatim
+Distance :: Pr :: SNR
+10 4.98265e-9 11289.3
+20 6.22831e-10 1411.16
+40 7.78539e-11 176.407
+60 2.30678e-11 52.2652
+80 9.73173e-12 22.0494
+100 4.98265e-12 11.2893
+200 6.22831e-13 1.41116
+500 3.98612e-14 .090314
+1000 4.98265e-15 .011289
+@end verbatim
+
+@subsection Validation test
+
+Test program available online at: @uref{http://xxx.xxx.com,,}
+
+Taken at default settings (packetSize = 1000, numPackets = 1, exponent = 3, reference loss = 46.6777, 802.11b at 2.4GHz)
+@verbatim
+Distance :: Pr :: snr
+10 4.98471e-9 11293.9
+20 6.23089e-10 1411.74
+40 7.78861e-11 176.468
+60 2.30774e-11 52.2868
+80 9.72576e-12 22.0585
+100 4.98471e-12 11.2939
+200 6.23089e-13 1.41174
+500 3.98777e-14 0.0903516
+1000 4.98471e-15 0.0112939
+@end verbatim
+
+
+@subsection Discussion
+There is a ~.04% error between these results. I do not believe this is
+due to rounding, as the results taken from the equation from the source
+match exactly with the Friis results taken at one less power of ten.
+(Friis and LogDistance can be modeled by Pt*Gt*Gr*lmb^2/((4*pi)^2*d^n*L),
+where n is the exponent. n is 2 for Friis, and 3 for logDistance, which
+accounts for the power of ten. ie: Friis at 100m is equivalent to LogDistance
+at 10m.) Perhaps the ns-3 takes the random number into account despite
+not being listed in the source.
+
diff --git a/doc/testing/testing-framework.texi b/doc/testing/testing-framework.texi
new file mode 100644
index 000000000..1540c2d6b
--- /dev/null
+++ b/doc/testing/testing-framework.texi
@@ -0,0 +1,567 @@
+@c ========================================================================
+@c Testing framework
+@c ========================================================================
+
+@node TestingFramework
+@chapter Testing Framework
+
+@node BuildBots
+@section Buildbots
+
+The @command{ns-3} testing framework is composed of several major pieces. At
+the highest level are the buildbots (build robots). If you are unfamiliar with
+this system look at @uref{http://djmitche.github.com/buildbot/docs/0.7.11/}.
+This is an open-source automated system that allows @command{ns-3} to be rebuilt
+and tested each time something has changed. By running the buildbots on a number
+of different systems we can ensure that @command{ns-3} builds and executes
+properly on all of its supported systems.
+
+Users (and developers) typically will not interact with the buildbot system other
+than to read its messages regarding test results. If a failure is detected in
+one of the automated build and test jobs, the buildbot will send an email to the
+@emph{ns-developers} mailing list. This email will look something like:
+
+@verbatim
+ The Buildbot has detected a new failure of osx-ppc-g++-4.2 on NsNam.
+ Full details are available at:
+ http://ns-regression.ee.washington.edu:8010/builders/osx-ppc-g%2B%2B-4.2/builds/0
+
+ Buildbot URL: http://ns-regression.ee.washington.edu:8010/
+
+ Buildslave for this Build: darwin-ppc
+
+ Build Reason: The web-page 'force build' button was pressed by 'ww': ww
+
+ Build Source Stamp: HEAD
+ Blamelist:
+
+ BUILD FAILED: failed shell_5 shell_6 shell_7 shell_8 shell_9 shell_10 shell_11 shell_12 shell_13 shell_14 shell_15
+
+ sincerely,
+ -The Buildbot
+@end verbatim
+
+In the full details URL shown in the email, one can search for the keyword
+@code{failed} and select the @code{stdio} link for the corresponding step to see
+the reason for the failure.
+
+The buildbot will do its job quietly if there are no errors, and the system will
+undergo build and test cycles every day to verify that all is well.
+
+@node Testpy
+@section Test.py
+The buildbots use a Python program, @command{test.py}, that is reponsible for
+running all of the tests and collecting the resulting reports into a human-
+readable form. This program is also available for use by users and developers
+as well.
+
+@command{test.py} is very flexible in allowing the user to specify the number
+and kind of tests to run; and also the amount and kind of output to generate.
+
+By default, @command{test.py} will run all available tests and report status
+back in a very concise form. Running the command,
+
+@verbatim
+ ./test.py
+@end verbatim
+
+will result in a number of @code{PASS}, @code{FAIL}, @code{CRASH} or @code{SKIP}
+indications followed by the kind of test that was run and its display name.
+
+@verbatim
+ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ 'build' finished successfully (0.939s)
+ FAIL: TestSuite ns3-wifi-propagation-loss-models
+ PASS: TestSuite object-name-service
+ PASS: TestSuite pcap-file-object
+ PASS: TestSuite ns3-tcp-cwnd
+ ...
+
+ PASS: TestSuite ns3-tcp-interoperability
+ PASS: Example csma-broadcast
+ PASS: Example csma-multicast
+@end verbatim
+
+This mode is indented to be used by users who are interested in determining if
+their distribution is working correctly, and by developers who are interested
+in determining if changes they have made have caused any regressions.
+
+If one specifies an optional output style, one can generate detailed descriptions
+of the tests and status. Available styles are @command{text} and @command{HTML}.
+The buildbots will select the HTML option to generate HTML test reports for the
+nightly builds using,
+
+@verbatim
+ ./test.py --html=nightly.html
+@end verbatim
+
+In this case, an HTML file named ``nightly.html'' would be created with a pretty
+summary of the testing done. A ``human readable'' format is available for users
+interested in the details.
+
+@verbatim
+ ./test.py --text=results.txt
+@end verbatim
+
+In the example above, the test suite checking the @command{ns-3} wireless
+device propagation loss models failed. By default no further information is
+provided.
+
+To further explore the failure, @command{test.py} allows a single test suite
+to be specified. Running the command,
+
+@verbatim
+ ./test.py --suite=ns3-wifi-propagation-loss-models
+@end verbatim
+
+results in that single test suite being run.
+
+@verbatim
+ FAIL: TestSuite ns3-wifi-propagation-loss-models
+@end verbatim
+
+To find detailed information regarding the failure, one must specify the kind
+of output desired. For example, most people will probably be interested in
+a text file:
+
+@verbatim
+ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
+@end verbatim
+
+This will result in that single test suite being run with the test status written to
+the file ``results.txt''.
+
+You should find something similar to the following in that file:
+
+@verbatim
+FAIL: Test Suite ``ns3-wifi-propagation-loss-models'' (real 0.02 user 0.01 system 0.00)
+ PASS: Test Case "Check ... Friis ... model ..." (real 0.01 user 0.00 system 0.00)
+ FAIL: Test Case "Check ... Log Distance ... model" (real 0.01 user 0.01 system 0.00)
+ Details:
+ Message: Got unexpected SNR value
+ Condition: [long description of what actually failed]
+ Actual: 176.395
+ Limit: 176.407 +- 0.0005
+ File: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
+ Line: 360
+@end verbatim
+
+Notice that the Test Suite is composed of two Test Cases. The first test case
+checked the Friis propagation loss model and passed. The second test case
+failed checking the Log Distance propagation model. In this case, an SNR of
+176.395 was found, and the test expected a value of 176.407 correct to three
+decimal places. The file which implemented the failing test is listed as well
+as the line of code which triggered the failure.
+
+If you desire, you could just as easily have written an HTML file using the
+@code{--html} option as described above.
+
+Typically a user will run all tests at least once after downloading
+@command{ns-3} to ensure that his or her enviornment has been built correctly
+and is generating correct results according to the test suites. Developers
+will typically run the test suites before and after making a change to ensure
+that they have not introduced a regression with their changes. In this case,
+developers may not want to run all tests, but only a subset. For example,
+the developer might only want to run the unit tests periodically while making
+changes to a repository. In this case, @code{test.py} can be told to constrain
+the types of tests being run to a particular class of tests. The follwoing
+command will result in only the unit tests being run:
+
+@verbatim
+ ./test.py --constrain=unit
+@end verbatim
+
+Similarly, the following command will result in only the example smoke tests
+being run:
+
+@verbatim
+ ./test.py --constrain=unit
+@end verbatim
+
+To see a quick list of the legal kinds of constraints, you can ask for them
+to be listed. The following command
+
+@verbatim
+ ./test.py --kinds
+@end verbatim
+
+will result in the following list being displayed:
+
+@verbatim
+ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ 'build' finished successfully (0.939s)Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ bvt: Build Verification Tests (to see if build completed successfully)
+ unit: Unit Tests (within modules to check basic functionality)
+ system: System Tests (spans modules to check integration of modules)
+ example: Examples (to see if example programs run successfully)
+ performance: Performance Tests (check to see if the system is as fast as expected)
+@end verbatim
+
+This list is displayed in increasing order of complexity of the tests. Any of these
+kinds of test can be provided as a constraint using the @code{--constraint} option.
+
+To see a quick list of all of the test suites available, you can ask for them
+to be listed. The following command,
+
+@verbatim
+ ./test.py --list
+@end verbatim
+
+will result in a list of the test suite being displayed, similar to :
+
+@verbatim
+ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ 'build' finished successfully (0.939s)
+ ns3-wifi-propagation-loss-models
+ ns3-tcp-cwnd
+ ns3-tcp-interoperability
+ pcap-file-object
+ object-name-service
+ random-number-generators
+@end verbatim
+
+Any of these listed suites can be selected to be run by itself using the
+@code{--suite} option as shown above.
+
+Similarly to test suites, one can run a single example program using the @code{--example}
+option.
+
+@verbatim
+ ./test.py --example=udp-echo
+@end verbatim
+
+results in that single example being run.
+
+@verbatim
+ PASS: Example udp-echo
+@end verbatim
+
+Normally when example programs are executed, they write a large amount of trace
+file data. This is normally saved to the base directory of the distribution
+(e.g., /home/user/ns-3-dev). When @command{test.py} runs an example, it really
+is completely unconcerned with the trace files. It just wants to to determine
+if the example can be built and run without error. Since this is the case, the
+trace files are written into a @code{/tmp/unchecked-traces} directory. If you
+run the above example, you should be able to find the associated
+@code{udp-echo.tr} and @code{udp-echo-n-1.pcap} files there.
+
+The list of available examples is defined by the contents of the ``examples''
+directory in the distribution. If you select an example for execution using
+the @code{--example} option, @code{test.py} will not make any attempt to decide
+if the example has been configured or not, it will just try to run it and
+report the result of the attempt.
+
+When @command{test.py} runs, by default it will first ensure that the system has
+been completely built. This can be defeated by selecting the @code{--nowaf}
+option.
+
+@verbatim
+ ./test.py --list --nowaf
+@end verbatim
+
+will result in a list of the currently built test suites being displayed, similar to :
+
+@verbatim
+ ns3-wifi-propagation-loss-models
+ ns3-tcp-cwnd
+ ns3-tcp-interoperability
+ pcap-file-object
+ object-name-service
+ random-number-generators
+@end verbatim
+
+Note the absence of the @command{Waf} build messages.
+
+Finally, @code{test.py} provides a @command{--verbose} option which will print
+large amounts of information about its progress. It is not expected that this
+will be terribly useful for most users.
+
+@node TestTaxonomy
+@section Test Taxonomy
+
+As mentioned above, tests are grouped into a number of broadly defined
+classifications to allow users to selectively run tests to address the different
+kinds of testing that need to be done.
+
+@itemize @bullet
+@item Build Verification Tests
+@item Unit Tests
+@item System Tests
+@item Examples
+@item Performance Tests
+@end itemize
+
+@node BuildVerificationTests
+@subsection Build Verification Tests
+
+These are relatively simple tests that are built along with the distribution
+and are used to make sure that the build is pretty much working. Our
+current unit tests live in the source files of the code they test and are
+built into the ns-3 modules; and so fit the description of BVTs. BVTs live
+in the same source code that is built into the ns-3 code. Our current tests
+are examples of this kind of test.
+
+@node UnitTests
+@subsection Unit Tests
+
+Unit tests are more involved tests that go into detail to make sure that a
+piece of code works as advertized in isolation. There is really no reason
+for this kind of test to be built into an ns-3 module. It turns out, for
+example, that the unit tests for the object name service are about the same
+size as the object name service code itself. Unit tests are tests that
+check a single bit of functionality that are not built into the ns-3 code,
+but live in the same directory as the code it tests. It is possible that
+these tests check integration of multiple implementation files in a module
+as well. The file src/core/names-test-suite.cc is an example of this kind
+of test. The file src/common/pcap-file-test-suite.cc is another example
+that uses a known good pcap file as a test vector file. This file is stored
+locally in the src/common directory.
+
+@node SystemTests
+@subsection System Tests
+
+System tests are those that involve more than one module in the system. We
+have lots of this kind of test running in our current regression framework,
+but they are overloaded examples. We provide a new place for this kind of
+test in the directory ``src/tests''. The file
+src/test/ns3tcp/ns3-interop-test-suite.cc is an example of this kind of
+test. It uses NSC TCP to test the ns-3 TCP implementation. Often there
+will be test vectors required for this kind of test, and they are stored in
+the directory where the test lives. For example,
+ns3tcp-interop-response-vectors.pcap is a file consisting of a number of TCP
+headers that are used as the expected responses of the ns-3 TCP under test
+to a stimulus generated by the NSC TCP which is used as a ``known good''
+implementation.
+
+@node Examples
+@subsection Examples
+
+The examples are tested by the framework to make sure they built and will
+run. Nothing is checked, and currently the pcap files are just written off
+into /tmp to be discarded. If the examples run (don't crash) they pass this
+smoke test.
+
+@node PerformanceTests
+@subsection Performance Tests
+
+Performance tests are those which exercise a particular part of the system
+and determine if the tests have executed to completion in a reasonable time.
+
+@node RunningTests
+@section Running Tests
+
+Tests are typically run using the high level @code{test.py} program. They
+can also be run ``manually'' using a low level test-runner executable directly
+from @code{waf}.
+
+@node RunningTestsUnderTestRunnerExecutable
+@section Running Tests Under the Test Runner Executable
+
+The test-runner is the bridge from generic Python code to @command{ns-3} code.
+It is written in C++ and uses the automatic test discovery process in the
+@command{ns-3} code to find and allow execution of all of the various tests.
+
+Although it may not be used directly very often, it is good to understand how
+@code{test.py} actually runs the various tests.
+
+In order to execute the test-runner, you run it like any other ns-3 executable
+-- using @code{waf}. To get a list of available options, you can type:
+
+@verbatim
+ ./waf --run "test-runner --help"
+@end verbatim
+
+You should see something like the following:
+
+@verbatim
+ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
+ 'build' finished successfully (0.353s)
+ --basedir=dir: Set the base directory (where to find src) to ``dir''
+ --constrain=test-type: Constrain checks to test suites of type ``test-type''
+ --help: Print this message
+ --kinds: List all of the available kinds of tests
+ --list: List all of the test suites (optionally constrained by test-type)
+ --out=file-name: Set the test status output file to ``file-name''
+ --suite=suite-name: Run the test suite named ``suite-name''
+ --verbose: Turn on messages in the run test suites
+@end verbatim
+
+There are a number of things available to you which will be familiar to you if
+you have looked at @command{test.py}. This should be expected since the test-
+runner is just an interface between @code{test.py} and @command{ns-3}. You
+may notice that example-related commands are missing here. That is because
+the examples are really not @command{ns-3} tests. @command{test.py} runs them
+as if they were to present a unified testing environment, but they are really
+completely different and not to be found here.
+
+One new option that appears here is the @code{--basedir} option. It turns out
+that the tests may need to reference the source directory of the @code{ns-3}
+distribution to find local data, so a base directory is always required to run
+a test. To run one of the tests directly from the test-runner, you will need
+to specify the test suite to run along with the base directory. So you could do,
+
+@verbatim
+ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"
+@end verbatim
+
+Note the ``backward'' quotation marks on the @code{pwd} command. This will run
+the @code{pcap-file-object} test quietly. The only indication that
+you will get that the test passed is the @emph{absence} of a message from
+@code{waf} saying that the program returned something other than a zero
+exit code. To get some output from the test, you need to specify an output
+file to which the tests will write their XML status using the @code{--out}
+option. You need to be careful interpreting the results because the test
+suites will @emph{append} results onto this file. Try,
+
+@verbatim
+ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml''
+@end verbatim
+
+If you look at the file @code{myfile.xml} you should see something like,
+
+@verbatim
+
+ pcap-file-object
+
+ Check to see that PcapFile::Open with mode ``w'' works
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+
+ Check to see that PcapFile::Open with mode ``r'' works
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+
+ Check to see that PcapFile::Open with mode ``a'' works
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+
+ Check to see that PcapFileHeader is managed correctly
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+
+ Check to see that PcapRecordHeader is managed correctly
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+
+ Check to see that PcapFile can read out a known good pcap file
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+ PASS
+ real 0.00 user 0.00 system 0.00
+
+@end verbatim
+
+If you are familiar with XML this should be fairly self-explanatory. It is
+also not a complete XML file since test suites are designed to have their
+output appended to a master XML status file as described in the @command{test.py}
+section.
+
+@node ClassTestRunner
+@section Class TestRunner
+
+The executables that run dedicated test programs use a TestRunner class. This
+class provides for automatic test registration and listing, as well as a way to
+exeute the individual tests. Individual test suites use C++ global constructors
+to add themselves to a collection of test suites managed by the test runner.
+The test runner is used to list all of the available tests and to select a test
+to be run. This is a quite simple class that provides three static methods to
+provide or Adding and Getting test suites to a collection of tests. See the
+doxygen for class @code{ns3::TestRunner} for details
+
+@node TestSuite
+@section Test Suite
+
+All @command{ns-3} tests are classified into Test Suites and Test Cases. A
+test suite is a collection of test cases that completely exercise a given kind
+of functionality. As described above, test suites can be classified as,
+
+@itemize @bullet
+@item Build Verification Tests
+@item Unit Tests
+@item System Tests
+@item Examples
+@item Performance Tests
+@end itemize
+
+This classification is exported from the TestSuite class. This class is quite
+simple, existing only as a place to export this type and to accumulate test
+cases. From a user perspective, in order to create a new TestSuite in the
+system one only has to define a new class that inherits from class @code{TestSuite}
+and perform these two duties.
+
+The following code will define a new class that can be run by @code{test.py}
+as a ``unit'' test with the display name, ``my-test-suite-name''.
+
+@verbatim
+ class MySuite : public TestSuite
+ {
+ public:
+ MyTestSuite ();
+ };
+
+ MyTestSuite::MyTestSuite ()
+ : TestSuite ("my-test-suite-name", UNIT)
+ {
+ AddTestCase (new MyTestCase);
+ }
+
+ MyTestSuite myTestSuite;
+@end verbatim
+
+The base class takes care of all of the registration and reporting required to
+be a good citizen in the test framework.
+
+@node TestCase
+@section Test Case
+
+Individual tests are created using a TestCase class. Common models for the use
+of a test case include "one test case per feature", and "one test case per method."
+Mixtures of these models may be used.
+
+In order to create a new test case in the system, all one has to do is to inherit
+from the @code{TestCase} base class, override the constructor to give the test
+case a name and override the @code{DoRun} method to run the test.
+
+@verbatim
+class MyTestCase : public TestCase
+{
+ MyTestCase ();
+ virtual bool DoRun (void);
+};
+
+MyTestCase::MyTestCase ()
+ : TestCase ("Check some bit of functionality")
+{
+}
+
+bool
+MyTestCase::DoRun (void)
+{
+ NS_TEST_ASSERT_MSG_EQ (true, true, "Some failure message");
+ return GetErrorStatus ();
+}
+@end verbatim
+
+@node Utilities
+@section Utilities
+
+There are a number of utilities of various kinds that are also part of the
+testing framework. Examples include a generalized pcap file useful for
+storing test vectors; a generic container useful for transient storage of
+test vectors during test execution; and tools for generating presentations
+based on validation and verification testing results.
+
+
+
+
+
diff --git a/doc/testing/testing.css b/doc/testing/testing.css
new file mode 100644
index 000000000..a7586ac83
--- /dev/null
+++ b/doc/testing/testing.css
@@ -0,0 +1,156 @@
+body {
+ font-family: "Trebuchet MS", "Bitstream Vera Sans", verdana, lucida, arial, helvetica, sans-serif;
+ background: white;
+ color: black;
+ font-size: 11pt;
+}
+
+h1, h2, h3, h4, h5, h6 {
+# color: #990000;
+ color: #009999;
+}
+
+pre {
+ font-size: 10pt;
+ background: #e0e0e0;
+ color: black;
+}
+
+a:link, a:visited {
+ font-weight: normal;
+ text-decoration: none;
+ color: #0047b9;
+}
+
+a:hover {
+ font-weight: normal;
+ text-decoration: underline;
+ color: #0047b9;
+}
+
+img {
+ border: 0px;
+}
+
+#main th {
+ font-size: 12pt;
+ background: #b0b0b0;
+}
+
+.odd {
+ font-size: 12pt;
+ background: white;
+}
+
+.even {
+ font-size: 12pt;
+ background: #e0e0e0;
+}
+
+.answer {
+ font-size: large;
+ font-weight: bold;
+}
+
+.answer p {
+ font-size: 12pt;
+ font-weight: normal;
+}
+
+.answer ul {
+ font-size: 12pt;
+ font-weight: normal;
+}
+
+#container {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0px;
+}
+
+#feedback {
+ color: #b0b0b0;
+ font-size: 9pt;
+ font-style: italic;
+}
+
+#header {
+ position: absolute;
+ margin: 0px;
+ top: 10px;
+ height:96px;
+ left: 175px;
+ right: 10em;
+ bottom: auto;
+ background: white;
+ clear: both;
+}
+
+#middle {
+ position: absolute;
+ left: 0;
+ height: auto;
+ width: 100%;
+}
+
+#main {
+ position: absolute;
+ top: 50px;
+ left: 175px;
+ right: 100px;
+ background: white;
+ padding: 0em 0em 0em 0em;
+}
+
+#navbar {
+ position: absolute;
+ top: 75px;
+ left: 0em;
+ width: 146px;
+ padding: 0px;
+ margin: 0px;
+ font-size: 10pt;
+}
+
+#navbar a:link, #navbar a:visited {
+ font-weight: normal;
+ text-decoration: none;
+ color: #0047b9;
+}
+
+#navbar a:hover {
+ font-weight: normal;
+ text-decoration: underline;
+ color: #0047b9;
+}
+
+#navbar dl {
+ width: 146px;
+ padding: 0;
+ margin: 0 0 10px 0px;
+ background: #99ffff url(images/box_bottom2.gif) no-repeat bottom left;
+}
+
+#navbar dt {
+ padding: 6px 10px;
+ font-size: 100%;
+ font-weight: bold;
+ background: #009999;
+ margin: 0px;
+ border-bottom: 1px solid #fff;
+ color: white;
+ background: #009999 url(images/box_top2.gif) no-repeat top left;
+}
+
+#navbar dd {
+ font-size: 100%;
+ margin: 0 0 0 0px;
+ padding: 6px 10px;
+ color: #0047b9;
+}
+
+dd#selected {
+ background: #99ffff url(images/arrow.gif) no-repeat;
+ background-position: 4px 10px;
+}
diff --git a/doc/testing/testing.texi b/doc/testing/testing.texi
new file mode 100644
index 000000000..47496ad7b
--- /dev/null
+++ b/doc/testing/testing.texi
@@ -0,0 +1,98 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ns-3.info
+@settitle ns-3 manual
+@c @setchapternewpage odd
+@c %**end of header
+
+@ifinfo
+Documentation for the @command{ns-3} project is available in
+several documents and the wiki:
+@itemize @bullet
+@item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen/Manual}: Documentation of the public APIs of the simulator
+@item @uref{http://www.nsnam.org/tutorial/index.html,,ns-3 Tutorial}
+@item @uref{http://www.nsnam.org/doc//index.html,,ns-3 Tutorial}
+@item Reference Manual
+@item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki}
+@end itemize
+
+This document is written in GNU Texinfo and is to be maintained in
+revision control on the @command{ns-3} code server. Both PDF and HTML versions
+should be available on the server. Changes to
+the document should be discussed on the ns-developers@@isi.edu mailing list.
+@end ifinfo
+
+@copying
+
+This is an @command{ns-3} reference manual.
+Primary documentation for the @command{ns-3} project is available in
+four forms:
+@itemize @bullet
+@item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen}: Documentation of the public APIs of the simulator
+@item @uref{http://www.nsnam.org/docs/tutorial/index.html,,ns-3 Tutorial}
+@item @uref{http://www.nsnam.org/docs/manual/index.html,,ns-3 Manual}
+@item Testing and Validation (this document)
+@item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki}
+@end itemize
+
+This document is written in GNU Texinfo and is to be maintained in
+revision control on the @command{ns-3} code server. Both PDF and HTML
+versions should be available on the server. Changes to
+the document should be discussed on the ns-developers@@isi.edu mailing list.
+
+This software is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This software is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see @uref{http://www.gnu.org/licenses/}.
+@end copying
+
+@titlepage
+@title ns-3 Testing and Validation
+@author ns-3 project
+@author feedback: ns-developers@@isi.edu
+@today{}
+
+@c @page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@c So the toc is printed at the start.
+@anchor{Full Table of Contents}
+@contents
+
+@ifnottex
+@node Top, Overview, Full Table of Contents
+@top ns-3 Manual (html version)
+
+For a pdf version of this document,
+see @uref{http://www.nsnam.org/docs/testing.pdf}.
+
+@insertcopying
+@end ifnottex
+
+@menu
+* Overview::
+* Background::
+* Testing framework::
+* How to write tests::
+* Propagation Loss Models::
+@end menu
+
+@include overview.texi
+@include background.texi
+@include testing-framework.texi
+@include how-to-write-tests.texi
+@include propagation-loss.texi
+
+@printindex cp
+
+@bye
diff --git a/examples/icmpv6-redirect.cc b/examples/icmpv6-redirect.cc
index c70150445..3ad79a68c 100644
--- a/examples/icmpv6-redirect.cc
+++ b/examples/icmpv6-redirect.cc
@@ -51,6 +51,10 @@ using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("Icmpv6RedirectExample");
+/**
+ * \class StackHelper
+ * \brief Helper to set or get some IPv6 information about nodes.
+ */
class StackHelper
{
public:
@@ -69,7 +73,7 @@ class StackHelper
routing = routingHelper.GetStaticRouting (ipv6);
std::cout << "Routing table of " << n << " : " << std::endl;
- std::cout << "Destination\t\t\t\t" << "Gateway\t\t\t\t\t" << "Interface\t" << std::endl;
+ std::cout << "Destination\t\t\t\t" << "Gateway\t\t\t\t\t" << "Interface\t" << "Prefix to use" << std::endl;
nbRoutes = routing->GetNRoutes ();
for(uint32_t i = 0 ; i < nbRoutes ; i++)
@@ -77,10 +81,19 @@ class StackHelper
route = routing->GetRoute (i);
std::cout << route.GetDest () << "\t"
<< route.GetGateway () << "\t"
- << route.GetInterface () << "\t" << std::endl;
+ << route.GetInterface () << "\t"
+ << route.GetPrefixToUse () << "\t"
+ << std::endl;
}
}
+ /**
+ * \brief Add an host route.
+ * \param n node
+ * \param dst destination address
+ * \param nextHop next hop for destination
+ * \param interface output interface
+ */
inline void AddHostRouteTo (Ptr& n, Ipv6Address dst, Ipv6Address nextHop, uint32_t interface)
{
Ptr routing = 0;
@@ -88,7 +101,6 @@ class StackHelper
Ptr ipv6 = n->GetObject ();
routing = routingHelper.GetStaticRouting (ipv6);
-
routing->AddHostRouteTo (dst, nextHop, interface);
}
};
diff --git a/examples/mesh.cc b/examples/mesh.cc
new file mode 100644
index 000000000..fa42ae62b
--- /dev/null
+++ b/examples/mesh.cc
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008,2009 IITP RAS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Kirill Andreev
+ *
+ *
+ * By default this script creates m_xSize * m_ySize square grid topology with
+ * IEEE802.11s stack installed at each node with peering management
+ * and HWMP protocol.
+ * The side of the square cell is defined by m_step parameter.
+ * When topology is created, UDP ping is installed to opposite corners
+ * by diagonals. packet size of the UDP ping and interval between two
+ * successive packets is configurable.
+ *
+ * m_xSize * step
+ * |<--------->|
+ * step
+ * |<--->|
+ * * --- * --- * <---Ping sink _
+ * | \ | / | ^
+ * | \ | / | |
+ * * --- * --- * m_ySize * step |
+ * | / | \ | |
+ * | / | \ | |
+ * * --- * --- * _
+ * ^ Ping source
+ *
+ * See also MeshTest::Configure to read more about configurable
+ * parameters.
+ */
+
+
+#include "ns3/core-module.h"
+#include "ns3/simulator-module.h"
+#include "ns3/node-module.h"
+#include "ns3/helper-module.h"
+#include "ns3/global-routing-module.h"
+#include "ns3/wifi-module.h"
+#include "ns3/mesh-module.h"
+#include "ns3/mobility-module.h"
+#include "ns3/mesh-helper.h"
+
+#include
+#include
+#include
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("TestMeshScript");
+class MeshTest
+{
+ public:
+ /// Init test
+ MeshTest ();
+ /// Configure test from command line arguments
+ void Configure (int argc, char ** argv);
+ /// Run test
+ int Run ();
+ private:
+ int m_xSize;
+ int m_ySize;
+ double m_step;
+ double m_randomStart;
+ double m_totalTime;
+ double m_packetInterval;
+ uint16_t m_packetSize;
+ uint32_t m_nIfaces;
+ bool m_chan;
+ bool m_pcap;
+ std::string m_stack;
+ std::string m_root;
+ /// List of network nodes
+ NodeContainer nodes;
+ /// List of all mesh point devices
+ NetDeviceContainer meshDevices;
+ //Addresses of interfaces:
+ Ipv4InterfaceContainer interfaces;
+ // MeshHelper. Report is not static methods
+ MeshHelper mesh;
+ private:
+ /// Create nodes and setup their mobility
+ void CreateNodes ();
+ /// Install internet m_stack on nodes
+ void InstallInternetStack ();
+ /// Install applications
+ void InstallApplication ();
+ /// Print mesh devices diagnostics
+ void Report ();
+};
+MeshTest::MeshTest () :
+ m_xSize (3),
+ m_ySize (3),
+ m_step (100.0),
+ m_randomStart (0.1),
+ m_totalTime (100.0),
+ m_packetInterval (0.1),
+ m_packetSize (1024),
+ m_nIfaces (1),
+ m_chan (true),
+ m_pcap (false),
+ m_stack ("ns3::Dot11sStack"),
+ m_root ("ff:ff:ff:ff:ff:ff")
+{
+}
+void
+MeshTest::Configure (int argc, char *argv[])
+{
+ CommandLine cmd;
+ cmd.AddValue ("x-size", "Number of nodes in a row grid. [6]", m_xSize);
+ cmd.AddValue ("y-size", "Number of rows in a grid. [6]", m_ySize);
+ cmd.AddValue ("step", "Size of edge in our grid, meters. [100 m]", m_step);
+ /*
+ * As soon as starting node means that it sends a beacon,
+ * simultaneous start is not good.
+ */
+ cmd.AddValue ("start", "Maximum random start delay, seconds. [0.1 s]", m_randomStart);
+ cmd.AddValue ("time", "Simulation time, seconds [100 s]", m_totalTime);
+ cmd.AddValue ("packet-interval", "Interval between packets in UDP ping, seconds [0.001 s]", m_packetInterval);
+ cmd.AddValue ("packet-size", "Size of packets in UDP ping", m_packetSize);
+ cmd.AddValue ("interfaces", "Number of radio interfaces used by each mesh point. [1]", m_nIfaces);
+ cmd.AddValue ("channels", "Use different frequency channels for different interfaces. [0]", m_chan);
+ cmd.AddValue ("pcap", "Enable PCAP traces on interfaces. [0]", m_pcap);
+ cmd.AddValue ("stack", "Type of protocol stack. ns3::Dot11sStack by default", m_stack);
+ cmd.AddValue ("root", "Mac address of root mesh point in HWMP", m_root);
+
+ cmd.Parse (argc, argv);
+ NS_LOG_DEBUG ("Grid:" << m_xSize << "*" << m_ySize);
+ NS_LOG_DEBUG ("Simulation time: " << m_totalTime << " s");
+}
+void
+MeshTest::CreateNodes ()
+{
+ /*
+ * Create m_ySize*m_xSize stations to form a grid topology
+ */
+ nodes.Create (m_ySize*m_xSize);
+ // Configure YansWifiChannel
+ YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default ();
+ YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
+ wifiPhy.SetChannel (wifiChannel.Create ());
+ /*
+ * Create mesh helper and set stack installer to it
+ * Stack installer creates all needed protocols and install them to
+ * mesh point device
+ */
+ mesh = MeshHelper::Default ();
+ mesh.SetStackInstaller (m_stack, "Root", Mac48AddressValue (Mac48Address (m_root.c_str ())));
+ if (m_chan)
+ {
+ mesh.SetSpreadInterfaceChannels (MeshHelper::SPREAD_CHANNELS);
+ }
+ else
+ {
+ mesh.SetSpreadInterfaceChannels (MeshHelper::ZERO_CHANNEL);
+ }
+ mesh.SetMacType ("RandomStart", TimeValue (Seconds(m_randomStart)));
+ // Set number of interfaces - default is single-interface mesh point
+ mesh.SetNumberOfInterfaces (m_nIfaces);
+ // Install protocols and return container if MeshPointDevices
+ meshDevices = mesh.Install (wifiPhy, nodes);
+ // Setup mobility - static grid topology
+ MobilityHelper mobility;
+ mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
+ "MinX", DoubleValue (0.0),
+ "MinY", DoubleValue (0.0),
+ "DeltaX", DoubleValue (m_step),
+ "DeltaY", DoubleValue (m_step),
+ "GridWidth", UintegerValue (m_xSize),
+ "LayoutType", StringValue ("RowFirst"));
+ mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
+ mobility.Install (nodes);
+ if (m_pcap)
+ wifiPhy.EnablePcapAll (std::string ("mp-"));
+}
+void
+MeshTest::InstallInternetStack ()
+{
+ InternetStackHelper internetStack;
+ internetStack.Install (nodes);
+ Ipv4AddressHelper address;
+ address.SetBase ("10.1.1.0", "255.255.255.0");
+ interfaces = address.Assign (meshDevices);
+}
+void
+MeshTest::InstallApplication ()
+{
+ UdpEchoServerHelper echoServer (9);
+ ApplicationContainer serverApps = echoServer.Install (nodes.Get (0));
+ serverApps.Start (Seconds (0.0));
+ serverApps.Stop (Seconds (m_totalTime));
+ UdpEchoClientHelper echoClient (interfaces.GetAddress (0), 9);
+ echoClient.SetAttribute ("MaxPackets", UintegerValue ((uint32_t)(m_totalTime*(1/m_packetInterval))));
+ echoClient.SetAttribute ("Interval", TimeValue (Seconds (m_packetInterval)));
+ echoClient.SetAttribute ("PacketSize", UintegerValue (m_packetSize));
+ ApplicationContainer clientApps = echoClient.Install (nodes.Get (m_xSize*m_ySize-1));
+ clientApps.Start (Seconds (0.0));
+ clientApps.Stop (Seconds (m_totalTime));
+}
+int
+MeshTest::Run ()
+{
+ CreateNodes ();
+ InstallInternetStack ();
+ InstallApplication ();
+ Simulator::Schedule (Seconds(m_totalTime), & MeshTest::Report, this);
+ Simulator::Stop (Seconds (m_totalTime));
+ Simulator::Run ();
+ Simulator::Destroy ();
+ return 0;
+}
+void
+MeshTest::Report ()
+{
+ unsigned n (0);
+ for (NetDeviceContainer::Iterator i = meshDevices.Begin (); i != meshDevices.End (); ++i, ++n)
+ {
+ std::ostringstream os;
+ os << "mp-report-" << n << ".xml";
+ std::cerr << "Printing mesh point device #" << n << " diagnostics to " << os.str () << "\n";
+ std::ofstream of;
+ of.open (os.str().c_str());
+ if (! of.is_open ())
+ {
+ std::cerr << "Error: Can't open file " << os.str() << "\n";
+ return;
+ }
+ mesh.Report (*i, of);
+ of.close ();
+ }
+}
+int
+main (int argc, char *argv[])
+{
+ MeshTest t;
+ t.Configure (argc, argv);
+ return t.Run();
+}
diff --git a/examples/radvd-two-prefix.cc b/examples/radvd-two-prefix.cc
index 592c05ba1..f382ba52c 100644
--- a/examples/radvd-two-prefix.cc
+++ b/examples/radvd-two-prefix.cc
@@ -46,10 +46,13 @@ using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("RadvdExample");
+/**
+ * \class StackHelper
+ * \brief Helper to set or get some IPv6 information about nodes.
+ */
class StackHelper
{
public:
-
/**
* \brief Add an address to a IPv6 node.
* \param n node
diff --git a/examples/simple-routing-ping6.cc b/examples/simple-routing-ping6.cc
index 480b72b33..f1c2ad800 100644
--- a/examples/simple-routing-ping6.cc
+++ b/examples/simple-routing-ping6.cc
@@ -39,6 +39,10 @@ using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("SimpleRoutingPing6Example");
+/**
+ * \class StackHelper
+ * \brief Helper to set or get some IPv6 information about nodes.
+ */
class StackHelper
{
public:
diff --git a/examples/test-ipv6.cc b/examples/test-ipv6.cc
index 117ecf882..ab90f1aa1 100644
--- a/examples/test-ipv6.cc
+++ b/examples/test-ipv6.cc
@@ -20,7 +20,6 @@
*/
#include "ns3/log.h"
-//#include "ns3/ipv6.h"
#include "ns3/ipv6-address.h"
#include "ns3/node.h"
#include "ns3/mac48-address.h"
@@ -38,24 +37,24 @@ main (int argc, char *argv[])
Mac48Address m_addresses[10];
- m_addresses[0]=("00:00:00:00:00:01");
- m_addresses[1]=("00:00:00:00:00:02");
- m_addresses[2]=("00:00:00:00:00:03");
- m_addresses[3]=("00:00:00:00:00:04");
- m_addresses[4]=("00:00:00:00:00:05");
- m_addresses[5]=("00:00:00:00:00:06");
- m_addresses[6]=("00:00:00:00:00:07");
- m_addresses[7]=("00:00:00:00:00:08");
- m_addresses[8]=("00:00:00:00:00:09");
- m_addresses[9]=("00:00:00:00:00:10");
+ m_addresses[0] = ("00:00:00:00:00:01");
+ m_addresses[1] = ("00:00:00:00:00:02");
+ m_addresses[2] = ("00:00:00:00:00:03");
+ m_addresses[3] = ("00:00:00:00:00:04");
+ m_addresses[4] = ("00:00:00:00:00:05");
+ m_addresses[5] = ("00:00:00:00:00:06");
+ m_addresses[6] = ("00:00:00:00:00:07");
+ m_addresses[7] = ("00:00:00:00:00:08");
+ m_addresses[8] = ("00:00:00:00:00:09");
+ m_addresses[9] = ("00:00:00:00:00:10");
Ipv6Address prefix1 ("2001:1::");
NS_LOG_INFO ("prefix = " << prefix1);
for (uint32_t i = 0; i < 10 ; ++i)
{
- NS_LOG_INFO ("address = " <GetInterface ()] = EventId ();
- ScheduleTransmit (Seconds (0.), (*it), m_eventIds[(*it)->GetInterface ()]);
+ ScheduleTransmit (Seconds (0.), (*it), m_eventIds[(*it)->GetInterface ()], Ipv6Address::GetAllNodesMulticast (), true);
}
}
@@ -119,13 +119,13 @@ void Radvd::AddConfiguration (Ptr routerInterface)
m_configurations.push_back (routerInterface);
}
-void Radvd::ScheduleTransmit (Time dt, Ptr config, EventId& eventId)
+void Radvd::ScheduleTransmit (Time dt, Ptr config, EventId& eventId, Ipv6Address dst, bool reschedule)
{
NS_LOG_FUNCTION (this << dt);
- eventId = Simulator::Schedule (dt, &Radvd::Send, this, config, Ipv6Address::GetAllNodesMulticast ());
+ eventId = Simulator::Schedule (dt, &Radvd::Send, this, config, dst, reschedule);
}
-void Radvd::Send (Ptr config, Ipv6Address dst)
+void Radvd::Send (Ptr config, Ipv6Address dst, bool reschedule)
{
NS_LOG_FUNCTION (this << dst);
NS_ASSERT (m_eventIds[config->GetInterface ()].IsExpired ());
@@ -212,10 +212,14 @@ void Radvd::Send (Ptr config, Ipv6Address dst)
NS_LOG_LOGIC ("Send RA");
m_socket->Send (p, 0);
- UniformVariable rnd;
- uint64_t delay = static_cast (rnd.GetValue (config->GetMinRtrAdvInterval (), config->GetMaxRtrAdvInterval ()) + 0.5);
- Time t = MilliSeconds (delay);
- ScheduleTransmit (t, config, m_eventIds[config->GetInterface ()]);
+ if (reschedule)
+ {
+ UniformVariable rnd;
+ uint64_t delay = static_cast (rnd.GetValue (config->GetMinRtrAdvInterval (), config->GetMaxRtrAdvInterval ()) + 0.5);
+ NS_LOG_INFO ("Reschedule in " << delay);
+ Time t = MilliSeconds (delay);
+ ScheduleTransmit (t, config, m_eventIds[config->GetInterface ()], Ipv6Address::GetAllNodesMulticast (), reschedule);
+ }
}
void Radvd::HandleRead (Ptr socket)
@@ -236,31 +240,28 @@ void Radvd::HandleRead (Ptr socket)
Time t;
packet->RemoveHeader (hdr);
-
switch (*packet->PeekData ())
{
case Icmpv6Header::ICMPV6_ND_ROUTER_SOLICITATION:
- /* send RA in response of a RS */
packet->RemoveHeader (rsHdr);
NS_LOG_INFO ("Received ICMPv6 Router Solicitation from " << hdr.GetSourceAddress () << " code = " << (uint32_t)rsHdr.GetCode ());
- delay = static_cast (rnd.GetValue (0, MAX_RA_DELAY_TIME) + 0.5);
- t = Simulator::Now () + MilliSeconds (delay);
-
-#if 0
- NS_LOG_INFO ("schedule new RA : " << t.GetTimeStep () << " next scheduled RA" << (int64_t)m_sendEvent.GetTs ());
-
- if (t.GetTimeStep () < static_cast (m_sendEvent.GetTs ()))
+ /* XXX advertise just prefix(es) for the interface not all */
+ for (RadvdInterfaceListCI it = m_configurations.begin () ; it != m_configurations.end () ; it++)
{
- /* send multicast RA */
- /* maybe replace this by a unicast RA (it is a SHOULD in the RFC) */
- NS_LOG_INFO ("Respond to RS");
- /* XXX advertise just the prefix for the interface not all */
- t = MilliSeconds (delay);
- /* XXX schedule packet send */
- /* ScheduleTransmit (t); */
+ /* calculate minimum delay between RA */
+ delay = static_cast (rnd.GetValue (0, MAX_RA_DELAY_TIME) + 0.5);
+ t = Simulator::Now () + MilliSeconds (delay); /* absolute time of solicited RA */
+
+ /* if our solicited RA is before the next periodic RA, we schedule it */
+ if (t.GetTimeStep () < static_cast (m_eventIds[(*it)->GetInterface ()].GetTs ()))
+ {
+ NS_LOG_INFO ("schedule new RA");
+ EventId ei;
+
+ ScheduleTransmit (MilliSeconds (delay), (*it), ei, address.GetIpv6 (), false);
+ }
}
-#endif
break;
default:
break;
diff --git a/src/applications/radvd/radvd.h b/src/applications/radvd/radvd.h
index e3e40dc8f..c301c636c 100644
--- a/src/applications/radvd/radvd.h
+++ b/src/applications/radvd/radvd.h
@@ -103,15 +103,18 @@ class Radvd : public Application
* \param dt interval between packet
* \param config interface configuration
* \param eventId event ID associated
+ * \param dst IPv6 destination address
+ * \param reschedule if true another send will be reschedule (periodic)
*/
- void ScheduleTransmit (Time dt, Ptr config, EventId& eventId);
+ void ScheduleTransmit (Time dt, Ptr config, EventId& eventId, Ipv6Address dst = Ipv6Address::GetAllNodesMulticast (), bool reschedule = false);
/**
* \brief Send a packet.
* \param config interface configuration
* \param dst destination address (default ff02::1)
+ * \param reschedule if true another send will be reschedule (periodic)
*/
- void Send (Ptr config, Ipv6Address dst = Ipv6Address::GetAllNodesMulticast ());
+ void Send (Ptr config, Ipv6Address dst = Ipv6Address::GetAllNodesMulticast (), bool reschedule = false);
/**
* \brief Handle received packet, especially router solicitation
diff --git a/src/common/known.pcap b/src/common/known.pcap
new file mode 100644
index 000000000..f5f6ae194
Binary files /dev/null and b/src/common/known.pcap differ
diff --git a/src/common/pcap-file-test-suite.cc b/src/common/pcap-file-test-suite.cc
new file mode 100644
index 000000000..c96f7135a
--- /dev/null
+++ b/src/common/pcap-file-test-suite.cc
@@ -0,0 +1,965 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+#include
+
+#include "ns3/test.h"
+#include "ns3/pcap-file.h"
+
+using namespace ns3;
+
+// ===========================================================================
+// Some utility functions for the tests.
+// ===========================================================================
+
+uint16_t
+Swap (uint16_t val)
+{
+ return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
+}
+
+uint32_t
+Swap (uint32_t val)
+{
+ return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
+}
+
+bool
+CheckFileExists (std::string filename)
+{
+ FILE * p = fopen (filename.c_str (), "rb");
+ if (p == 0)
+ {
+ return false;
+ }
+
+ fclose (p);
+ return true;
+}
+
+
+bool
+CheckFileLength (std::string filename, uint64_t sizeExpected)
+{
+ FILE * p = fopen (filename.c_str (), "rb");
+ if (p == 0)
+ {
+ return false;
+ }
+
+ fseek (p, 0, SEEK_END);
+
+ uint64_t sizeActual = ftell (p);
+ fclose (p);
+
+ return sizeActual == sizeExpected;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can do its most basic job
+// and create an empty pcap file.
+// ===========================================================================
+class WriteModeCreateTestCase : public TestCase
+{
+public:
+ WriteModeCreateTestCase ();
+ virtual ~WriteModeCreateTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+WriteModeCreateTestCase::WriteModeCreateTestCase ()
+ : TestCase ("Check to see that PcapFile::Open with mode \"w\" works")
+{
+}
+
+WriteModeCreateTestCase::~WriteModeCreateTestCase ()
+{
+}
+
+void
+WriteModeCreateTestCase::DoSetup (void)
+{
+ std::stringstream filename;
+ uint32_t n = rand ();
+ filename << n;
+ m_testFilename = "/tmp/" + filename.str () + ".pcap";
+}
+
+void
+WriteModeCreateTestCase::DoTeardown (void)
+{
+ remove (m_testFilename.c_str ());
+}
+
+bool
+WriteModeCreateTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ // Opening a new file in write mode should result in an empty file of the
+ // given name.
+ //
+ bool err = f.Open (m_testFilename, "w");
+
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+ f.Close ();
+
+ NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), true,
+ "Open (" << m_testFilename << ", \"w\") does not create file");
+ NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 0), true,
+ "Open (" << m_testFilename << ", \"w\") does not result in an empty file");
+
+ //
+ // Calling Init() on a file created with "w" should result in a file just
+ // long enough to contain the pcap file header.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ err = f.Init (1234, 5678, 7);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
+
+ f.Close ();
+
+ NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 24), true,
+ "Init () does not result in a file with a pcap file header");
+
+ //
+ // Opening an existing file in write mode should result in that file being
+ // emptied.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ f.Close ();
+
+ NS_TEST_ASSERT_MSG_EQ (CheckFileLength (m_testFilename, 0), true,
+ "Open (" << m_testFilename << ", \"w\") does not result in an empty file");
+
+ //
+ // Initialize the file again.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false,
+ "Open (" << m_testFilename << ", \"w\") returns error");
+
+ err = f.Init (1234, 5678, 7);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
+
+ //
+ // Now we should be able to write to it since it was opened in "w" mode.
+ // This is just a permissions check so we don't actually look at the
+ // data.
+ //
+ uint8_t buffer[128];
+ err = f.Write (0, 0, buffer, 128);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
+
+ return false;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can open an existing pcap
+// file.
+// ===========================================================================
+class ReadModeCreateTestCase : public TestCase
+{
+public:
+ ReadModeCreateTestCase ();
+ virtual ~ReadModeCreateTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+ReadModeCreateTestCase::ReadModeCreateTestCase ()
+ : TestCase ("Check to see that PcapFile::Open with mode \"r\" works")
+{
+}
+
+ReadModeCreateTestCase::~ReadModeCreateTestCase ()
+{
+}
+
+void
+ReadModeCreateTestCase::DoSetup (void)
+{
+ std::stringstream filename;
+ uint32_t n = rand ();
+ filename << n;
+ m_testFilename = "/tmp/" + filename.str () + ".pcap";
+}
+
+void
+ReadModeCreateTestCase::DoTeardown (void)
+{
+ remove (m_testFilename.c_str ());
+}
+
+bool
+ReadModeCreateTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ // Opening a non-existing file in read mode should result in an error.
+ //
+ bool err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-existing-filename " << m_testFilename << ", \"r\") does not return error");
+
+ NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), false,
+ "Open (" << m_testFilename << ", \"r\") unexpectedly created a file");
+
+ //
+ // Okay, now create an uninitialized file using previously tested operations
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (filename, \"w\") returns error");
+ f.Close ();
+
+ //
+ // Opening this file should result in an error since it has no pcap file header.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-initialized-filename " << m_testFilename << ", \"r\") does not return error");
+
+ //
+ // Okay, now open that non-initialized file in write mode and initialize it
+ // Note that we open it in write mode to initialize it.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ err = f.Init (1234, 5678, 7);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
+ f.Close ();
+
+ //
+ // Opening this file should now work since it has a pcap file header.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (initialized-filename " << m_testFilename << ", \"r\") returns error");
+
+ //
+ // Now we should not be able to write to it since it was opened in "r" mode
+ // even if it has been initialized..
+ //
+ uint8_t buffer[128];
+ err = f.Write (0, 0, buffer, 128);
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Write (read-only-file " << m_testFilename << ") does not return error");
+
+ f.Close ();
+
+ return false;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can open an existing pcap
+// file for appending.
+// ===========================================================================
+class AppendModeCreateTestCase : public TestCase
+{
+public:
+ AppendModeCreateTestCase ();
+ virtual ~AppendModeCreateTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+AppendModeCreateTestCase::AppendModeCreateTestCase ()
+ : TestCase ("Check to see that PcapFile::Open with mode \"a\" works")
+{
+}
+
+AppendModeCreateTestCase::~AppendModeCreateTestCase ()
+{
+}
+
+void
+AppendModeCreateTestCase::DoSetup (void)
+{
+ std::stringstream filename;
+ uint32_t n = rand ();
+ filename << n;
+ m_testFilename = "/tmp/" + filename.str () + ".pcap";
+}
+
+void
+AppendModeCreateTestCase::DoTeardown (void)
+{
+ remove (m_testFilename.c_str ());
+}
+
+bool
+AppendModeCreateTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ // Opening a non-existing file in append mode should result in an error.
+ //
+ bool err = f.Open (m_testFilename, "a");
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-existing-filename " << m_testFilename << ", \"a\") does not return error");
+ f.Close ();
+
+ NS_TEST_ASSERT_MSG_EQ (CheckFileExists (m_testFilename), false,
+ "Open (" << m_testFilename << ", \"a\") unexpectedly created a file");
+
+ //
+ // Okay, now create an uninitialized file using previously tested operations
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+ f.Close ();
+
+ //
+ // Opening this file should result in an error since it has no pcap file header.
+ //
+ err = f.Open (m_testFilename, "a");
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Open (non-initialized-filename " << m_testFilename << ", \"a\") does not return error");
+
+ //
+ // Okay, now open that non-initialized file in write mode and initialize it.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (non-initialized-filename " << m_testFilename << ", \"w\") returns error");
+
+ err = f.Init (1234, 5678, 7);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
+ f.Close ();
+
+ //
+ // Opening this file should now work since it has a pcap file header.
+ //
+ err = f.Open (m_testFilename, "a");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (initialized-filename " << m_testFilename << ", \"r\") returns error");
+
+ //
+ // We should be able to write to it since it was opened in "a" mode.
+ //
+ uint8_t buffer[128];
+ err = f.Write (0, 0, buffer, 128);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Write (append-mode-file " << m_testFilename << ") returns error");
+
+ f.Close ();
+
+ return false;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can write out correct pcap
+// file headers in both endian cases, and then read them in correctly.
+// ===========================================================================
+class FileHeaderTestCase : public TestCase
+{
+public:
+ FileHeaderTestCase ();
+ virtual ~FileHeaderTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+FileHeaderTestCase::FileHeaderTestCase ()
+ : TestCase ("Check to see that PcapFileHeader is managed correctly")
+{
+}
+
+FileHeaderTestCase::~FileHeaderTestCase ()
+{
+}
+
+void
+FileHeaderTestCase::DoSetup (void)
+{
+ std::stringstream filename;
+ uint32_t n = rand ();
+ filename << n;
+ m_testFilename = "/tmp/" + filename.str () + ".pcap";
+}
+
+void
+FileHeaderTestCase::DoTeardown (void)
+{
+ remove (m_testFilename.c_str ());
+}
+
+bool
+FileHeaderTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ // Create an uninitialized file using previously tested operations
+ //
+ bool err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ //
+ // Initialize the pcap file header.
+ //
+ err = f.Init (1234, 5678, 7);
+ NS_TEST_ASSERT_MSG_EQ (err, false,
+ "Init (1234, 5678, 7) returns error");
+ f.Close ();
+
+ //
+ // Take a look and see what was done to the file
+ //
+ FILE *p = fopen (m_testFilename.c_str (), "r+b");
+ NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
+
+ uint32_t val32;
+ uint16_t val16;
+
+ size_t result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
+ NS_TEST_ASSERT_MSG_EQ (val32, 0xa1b2c3d4, "Magic number written incorrectly");
+
+ result = fread (&val16, sizeof(val16), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
+ NS_TEST_ASSERT_MSG_EQ (val16, 2, "Version major written incorrectly");
+
+ result = fread (&val16, sizeof(val16), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
+ NS_TEST_ASSERT_MSG_EQ (val16, 4, "Version minor written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
+ NS_TEST_ASSERT_MSG_EQ (val32, 7, "Version minor written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
+ NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
+ NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Snap length written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
+ NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Data length type written incorrectly");
+
+ fclose (p);
+ p = 0;
+
+ //
+ // We wrote a native-endian file out correctly, now let's see if we can read
+ // it back in correctly.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (existing-initialized-file " << m_testFilename << ", \"r\") returns error");
+
+ NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
+
+ //
+ // Re-open the file to erase its contents.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ //
+ // Initialize the pcap file header, turning on swap mode manually to force
+ // the pcap file header to be written out in foreign-endian form, whichever
+ // endian-ness that might be.
+ //
+ err = f.Init (1234, 5678, 7, true);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (1234, 5678, 7) returns error");
+ f.Close ();
+
+ //
+ // Take a look and see what was done to the file. Everything should now
+ // appear byte-swapped.
+ //
+ p = fopen (m_testFilename.c_str (), "r+b");
+ NS_TEST_ASSERT_MSG_NE (p, 0, "fopen(" << m_testFilename << ") should have been able to open a correctly created pcap file");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() magic number");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (0xa1b2c3d4)), "Magic number written incorrectly");
+
+ result = fread (&val16, sizeof(val16), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version major");
+ NS_TEST_ASSERT_MSG_EQ (val16, Swap(uint16_t (2)), "Version major written incorrectly");
+
+ result = fread (&val16, sizeof(val16), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() version minor");
+ NS_TEST_ASSERT_MSG_EQ (val16, Swap(uint16_t (4)), "Version minor written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() time zone correction");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (7)), "Version minor written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() sig figs");
+ NS_TEST_ASSERT_MSG_EQ (val32, 0, "Sig figs written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() snap length");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (5678)), "Snap length written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() data link type");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap(uint32_t (1234)), "Data length type written incorrectly");
+
+ fclose (p);
+ p = 0;
+
+ //
+ // We wrote an opposite-endian file out correctly, now let's see if we can read
+ // it back in correctly.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (existing-initialized-file " << m_testFilename << ", \"r\") returns error");
+
+ NS_TEST_ASSERT_MSG_EQ (f.GetSwapMode (), true, "Byte-swapped file not correctly indicated");
+
+ NS_TEST_ASSERT_MSG_EQ (f.GetMagic (), 0xa1b2c3d4, "Read back magic number incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetVersionMajor (), 2, "Read back version major incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetVersionMinor (), 4, "Read back version minor incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetTimeZoneOffset (), 7, "Read back time zone offset incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetSigFigs (), 0, "Read back sig figs incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetSnapLen (), 5678, "Read back snap len incorrectly");
+ NS_TEST_ASSERT_MSG_EQ (f.GetDataLinkType (), 1234, "Read back data link type incorrectly");
+
+ f.Close ();
+
+ return false;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can write pcap packet
+// records in both endian cases, and then read them in correctly.
+// ===========================================================================
+class RecordHeaderTestCase : public TestCase
+{
+public:
+ RecordHeaderTestCase ();
+ virtual ~RecordHeaderTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+RecordHeaderTestCase::RecordHeaderTestCase ()
+ : TestCase ("Check to see that PcapRecordHeader is managed correctly")
+{
+}
+
+RecordHeaderTestCase::~RecordHeaderTestCase ()
+{
+}
+
+void
+RecordHeaderTestCase::DoSetup (void)
+{
+ std::stringstream filename;
+ uint32_t n = rand ();
+ filename << n;
+ m_testFilename = "/tmp/" + filename.str () + ".pcap";
+}
+
+void
+RecordHeaderTestCase::DoTeardown (void)
+{
+ remove (m_testFilename.c_str ());
+}
+
+bool
+RecordHeaderTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ // Create an uninitialized file using previously tested operations
+ //
+ bool err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ //
+ // Initialize the pcap file header.
+ //
+ err = f.Init (37, 43, -7);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (37, 43, -7) returns error");
+
+ //
+ // Initialize a buffer with a counting pattern to check the data later.
+ //
+ uint8_t bufferOut[128];
+ for (uint32_t i = 0; i < 128; ++i)
+ {
+ bufferOut[i] = i;
+ }
+
+ //
+ // Now we should be able to write a packet to it since it was opened in "w"
+ // mode. The packet data written should be limited to 43 bytes in length
+ // by the Init() call above.
+ //
+ err = f.Write (1234, 5678, bufferOut, 128);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
+ f.Close ();
+
+ //
+ // Let's peek into the file and see what actually went out for that
+ // packet.
+ //
+ FILE *p = fopen (m_testFilename.c_str (), "r+b");
+ NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
+
+ //
+ // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
+ // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
+ // double check that this is exactly what happened.
+ //
+ fseek (p, 0, SEEK_END);
+ uint64_t size = ftell (p);
+ NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
+
+ //
+ // A pcap file header takes up 24 bytes, so we should see a pcap record header
+ // starting there in the file. We've tested this all before so we just assume
+ // it's all right and just seek to just past that point..
+ //
+ fseek (p, 24, SEEK_SET);
+
+ uint32_t val32;
+
+ size_t result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
+ NS_TEST_ASSERT_MSG_EQ (val32, 1234, "Seconds timestamp written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
+ NS_TEST_ASSERT_MSG_EQ (val32, 5678, "Microseconds timestamp written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
+ NS_TEST_ASSERT_MSG_EQ (val32, 43, "Included length written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
+ NS_TEST_ASSERT_MSG_EQ (val32, 128, "Actual length written incorrectly");
+
+ //
+ // Take a look and see what went out into the file. The packet data
+ // should be unchanged (unswapped).
+ //
+ uint8_t bufferIn[128];
+
+ result = fread (bufferIn, 1, 43, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
+
+ for (uint32_t i = 0; i < 43; ++i)
+ {
+ NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
+ }
+
+ fclose (p);
+ p = 0;
+
+ //
+ // Let's see if the PcapFile object can figure out how to do the same thing
+ // correctly read in a packet.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"r\") of existing good file returns error");
+
+ uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
+
+ err = f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good packet returns error");
+ NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
+
+ //
+ // Did the data come back correctly?
+ //
+ for (uint32_t i = 0; i < 43; ++i)
+ {
+ NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
+ }
+
+ //
+ // We have to check to make sure that the pcap record header is swapped
+ // correctly. Open the file in write mode to clear the data.
+ //
+ err = f.Open (m_testFilename, "w");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"w\") returns error");
+
+ //
+ // Initialize the pcap file header, forcing the object into swap mode.
+ //
+ err = f.Init (37, 43, -7, true);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Init (37, 43, -7) returns error");
+
+ //
+ // Now we should be able to write a packet to it since it was opened in "w"
+ // mode. The packet data written should be limited to 43 bytes in length
+ // by the Init() call above.
+ //
+ err = f.Write (1234, 5678, bufferOut, 128);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Write (write-only-file " << m_testFilename << ") returns error");
+ f.Close ();
+
+ //
+ // Let's peek into the file and see what actually went out for that
+ // packet.
+ //
+ p = fopen (m_testFilename.c_str (), "r+b");
+ NS_TEST_ASSERT_MSG_NE (p, 0, "fopen() should have been able to open a correctly created pcap file");
+
+ //
+ // A pcap file header takes up 24 bytes, a pcap record header takes up 16 bytes
+ // and we wrote in 43 bytes, so the file must be 83 bytes long. Let's just
+ // double check that this is exactly what happened.
+ //
+ fseek (p, 0, SEEK_END);
+ size = ftell (p);
+ NS_TEST_ASSERT_MSG_EQ (size, 83, "Pcap file with one 43 byte packet is incorrect size");
+
+ //
+ // A pcap file header takes up 24 bytes, so we should see a pcap record header
+ // starting there in the file. We've tested this all before so we just assume
+ // it's all right and just seek past it.
+ //
+ fseek (p, 24, SEEK_SET);
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() seconds timestamp");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (1234)), "Swapped seconds timestamp written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() microseconds timestamp");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (5678)), "Swapped microseconds timestamp written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() included length");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (43)), "Swapped included length written incorrectly");
+
+ result = fread (&val32, sizeof(val32), 1, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 1, "Unable to fread() actual length");
+ NS_TEST_ASSERT_MSG_EQ (val32, Swap (uint32_t (128)), "Swapped Actual length written incorrectly");
+
+ //
+ // Take a look and see what went out into the file. The packet data
+ // should be unchanged (unswapped).
+ //
+ result = fread (bufferIn, 1, 43, p);
+ NS_TEST_ASSERT_MSG_EQ (result, 43, "Unable to fread() packet data of expected length");
+
+ for (uint32_t i = 0; i < 43; ++i)
+ {
+ NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data written");
+ }
+
+ fclose (p);
+ p = 0;
+
+ //
+ // Let's see if the PcapFile object can figure out how to do the same thing and
+ // correctly read in a packet. The record header info should come back to us
+ // swapped back into correct form.
+ //
+ err = f.Open (m_testFilename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << m_testFilename << ", \"r\") of existing good file returns error");
+
+ err = f.Read (bufferIn, sizeof(bufferIn), tsSec, tsUsec, inclLen, origLen, readLen);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good packet returns error");
+ NS_TEST_ASSERT_MSG_EQ (tsSec, 1234, "Incorrectly read seconds timestap from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (tsUsec, 5678, "Incorrectly read microseconds timestap from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (inclLen, 43, "Incorrectly read included length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (origLen, 128, "Incorrectly read original length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (readLen, 43, "Incorrectly constructed actual read length from known good packet given buffer size");
+
+ //
+ // Did the data come back correctly (unchanged / unswapped)?
+ //
+ for (uint32_t i = 0; i < 43; ++i)
+ {
+ NS_TEST_ASSERT_MSG_EQ (bufferIn[i], bufferOut[i], "Incorrect packet data read from known good packet");
+ }
+
+ f.Close ();
+
+ return false;
+}
+
+// ===========================================================================
+// Test case to make sure that the Pcap File Object can read out the contents
+// of a known good pcap file.
+// ===========================================================================
+class ReadFileTestCase : public TestCase
+{
+public:
+ ReadFileTestCase ();
+ virtual ~ReadFileTestCase ();
+
+private:
+ virtual void DoSetup (void);
+ virtual bool DoRun (void);
+ virtual void DoTeardown (void);
+
+ std::string m_testFilename;
+};
+
+ReadFileTestCase::ReadFileTestCase ()
+ : TestCase ("Check to see that PcapFile can read out a known good pcap file")
+{
+}
+
+ReadFileTestCase::~ReadFileTestCase ()
+{
+}
+
+void
+ReadFileTestCase::DoSetup (void)
+{
+}
+
+void
+ReadFileTestCase::DoTeardown (void)
+{
+}
+
+const uint32_t N_KNOWN_PACKETS = 6;
+const uint32_t N_PACKET_BYTES = 16;
+
+typedef struct PACKET_ENTRY {
+ uint32_t tsSec;
+ uint32_t tsUsec;
+ uint32_t inclLen;
+ uint32_t origLen;
+ uint16_t data[N_PACKET_BYTES];
+} PacketEntry;
+
+PacketEntry knownPackets[] = {
+ {2, 3696, 46, 46, {0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0003, 0x0a01,
+ 0x0201, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0204, 0x0000, 0x0000}},
+ {2, 3707, 46, 46, {0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0006, 0x0a01,
+ 0x0204, 0x0000, 0x0000, 0x0003, 0x0a01, 0x0201, 0x0000, 0x0000}},
+ {2, 3801, 1070, 1070, {0x4500, 0x041c, 0x0000, 0x0000, 0x3f11, 0x0000, 0x0a01, 0x0101,
+ 0x0a01, 0x0204, 0xc001, 0x0009, 0x0408, 0x0000, 0x0000, 0x0000}},
+ {2, 3811, 46, 46, {0x0001, 0x0800, 0x0604, 0x0001, 0x0000, 0x0000, 0x0006, 0x0a01,
+ 0x0204, 0xffff, 0xffff, 0xffff, 0x0a01, 0x0201, 0x0000, 0x0000}},
+ {2, 3822, 46, 46, {0x0001, 0x0800, 0x0604, 0x0002, 0x0000, 0x0000, 0x0003, 0x0a01,
+ 0x0201, 0x0000, 0x0000, 0x0006, 0x0a01, 0x0204, 0x0000, 0x0000}},
+ {2, 3915, 1070, 1070, {0x4500, 0x041c, 0x0000, 0x0000, 0x4011, 0x0000, 0x0a01, 0x0204,
+ 0x0a01, 0x0101, 0x0009, 0xc001, 0x0408, 0x0000, 0x0000, 0x0000}}
+};
+
+
+bool
+ReadFileTestCase::DoRun (void)
+{
+ PcapFile f;
+
+ //
+ //
+ std::string filename = NS_TEST_SOURCEDIR + "known.pcap";
+ bool err = f.Open (filename, "r");
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Open (" << filename << ", \"w\") returns error");
+
+ //
+ // We are going to read out the file header and all of the packets to make
+ // sure that we read what we know, a priori, to be there.
+ //
+ // The packet data was gotten using "tcpdump -nn -tt -r known.pcap -x"
+ // and the timestamp and first 32 bytes of the resulting dump were
+ // duplicated in the structure above.
+ //
+ uint8_t data[N_PACKET_BYTES];
+ uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
+
+ PacketEntry *p = knownPackets;
+
+ for (uint32_t i = 0; i < N_KNOWN_PACKETS; ++i, ++p)
+ {
+ err = f.Read (data, sizeof(data), tsSec, tsUsec, inclLen, origLen, readLen);
+ NS_TEST_ASSERT_MSG_EQ (err, false, "Read() of known good pcap file returns error");
+ NS_TEST_ASSERT_MSG_EQ (tsSec, p->tsSec, "Incorrectly read seconds timestap from known good pcap file");
+ NS_TEST_ASSERT_MSG_EQ (tsUsec, p->tsUsec, "Incorrectly read microseconds timestap from known good pcap file");
+ NS_TEST_ASSERT_MSG_EQ (inclLen, p->inclLen, "Incorrectly read included length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (origLen, p->origLen, "Incorrectly read original length from known good packet");
+ NS_TEST_ASSERT_MSG_EQ (readLen, N_PACKET_BYTES, "Incorrect actual read length from known good packet given buffer size");
+ }
+
+ //
+ // The file should now be at EOF since we've read all of the packets.
+ // Another packet read should return an error.
+ //
+ err = f.Read (data, 1, tsSec, tsUsec, inclLen, origLen, readLen);
+ NS_TEST_ASSERT_MSG_EQ (err, true, "Read() of known good pcap file at EOF does not return error");
+
+ f.Close ();
+
+ return false;
+}
+
+class PcapFileTestSuite : public TestSuite
+{
+public:
+ PcapFileTestSuite ();
+};
+
+PcapFileTestSuite::PcapFileTestSuite ()
+ : TestSuite ("pcap-file-object", UNIT)
+{
+ AddTestCase (new WriteModeCreateTestCase);
+ AddTestCase (new ReadModeCreateTestCase);
+ AddTestCase (new AppendModeCreateTestCase);
+ AddTestCase (new FileHeaderTestCase);
+ AddTestCase (new RecordHeaderTestCase);
+ AddTestCase (new ReadFileTestCase);
+}
+
+PcapFileTestSuite pcapFileTestSuite;
diff --git a/src/common/pcap-file.cc b/src/common/pcap-file.cc
new file mode 100644
index 000000000..c652d9808
--- /dev/null
+++ b/src/common/pcap-file.cc
@@ -0,0 +1,519 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2009 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include
+#include
+
+#include "pcap-file.h"
+
+//
+// This file is used as part of the ns-3 test framework, so please refrain from
+// adding any ns-3 specific constructs such as Packet to this file.
+//
+namespace ns3 {
+
+const uint32_t MAGIC = 0xa1b2c3d4; /**< Magic number identifying standard pcap file format */
+const uint32_t SWAPPED_MAGIC = 0xd4c3b2a1; /**< Looks this way if byte swapping is required */
+
+const uint32_t NS_MAGIC = 0xa1b23cd4; /**< Magic number identifying nanosec resolution pcap file format */
+const uint32_t NS_SWAPPED_MAGIC = 0xd43cb2a1; /**< Looks this way if byte swapping is required */
+
+const uint16_t VERSION_MAJOR = 2; /**< Major version of supported pcap file format */
+const uint16_t VERSION_MINOR = 4; /**< Minor version of supported pcap file format */
+const int32_t SIGFIGS_DEFAULT = 0; /**< Significant figures for timestamps (libpcap doesn't even bother) */
+
+PcapFile::PcapFile ()
+ : m_filename (""),
+ m_filePtr (0),
+ m_haveFileHeader (false),
+ m_swapMode (false)
+{
+}
+
+PcapFile::~PcapFile ()
+{
+ Close ();
+}
+
+void
+PcapFile::Close (void)
+{
+ if (m_filePtr)
+ {
+ fclose (m_filePtr);
+ }
+ m_filePtr = 0;
+ m_filename = "";
+ m_haveFileHeader = false;
+}
+
+uint32_t
+PcapFile::GetMagic (void)
+{
+ return m_fileHeader.m_magicNumber;
+}
+
+uint16_t
+PcapFile::GetVersionMajor (void)
+{
+ return m_fileHeader.m_versionMajor;
+}
+
+uint16_t
+PcapFile::GetVersionMinor (void)
+{
+ return m_fileHeader.m_versionMinor;
+}
+
+int32_t
+PcapFile::GetTimeZoneOffset (void)
+{
+ return m_fileHeader.m_zone;
+}
+
+uint32_t
+PcapFile::GetSigFigs (void)
+{
+ return m_fileHeader.m_sigFigs;
+}
+
+uint32_t
+PcapFile::GetSnapLen (void)
+{
+ return m_fileHeader.m_snapLen;
+}
+
+uint32_t
+PcapFile::GetDataLinkType (void)
+{
+ return m_fileHeader.m_type;
+}
+
+bool
+PcapFile::GetSwapMode (void)
+{
+ return m_swapMode;
+}
+
+uint8_t
+PcapFile::Swap (uint8_t val)
+{
+ return val;
+}
+
+uint16_t
+PcapFile::Swap (uint16_t val)
+{
+ return ((val >> 8) & 0x00ff) | ((val << 8) & 0xff00);
+}
+
+uint32_t
+PcapFile::Swap (uint32_t val)
+{
+ return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000);
+}
+
+void
+PcapFile::Swap (PcapFileHeader *from, PcapFileHeader *to)
+{
+ to->m_magicNumber = Swap (from->m_magicNumber);
+ to->m_versionMajor = Swap (from->m_versionMajor);
+ to->m_versionMinor = Swap (from->m_versionMinor);
+ to->m_zone = Swap (uint32_t(from->m_zone));
+ to->m_sigFigs = Swap (from->m_sigFigs);
+ to->m_snapLen = Swap (from->m_snapLen);
+ to->m_type = Swap (from->m_type);
+}
+
+void
+PcapFile::Swap (PcapRecordHeader *from, PcapRecordHeader *to)
+{
+ to->m_tsSec = Swap (from->m_tsSec);
+ to->m_tsUsec = Swap (from->m_tsUsec);
+ to->m_inclLen = Swap (from->m_inclLen);
+ to->m_origLen = Swap (from->m_origLen);
+}
+
+bool
+PcapFile::WriteFileHeader (void)
+{
+ //
+ // If we're initializing the file, we need to write the pcap file header
+ // at the start of the file.
+ //
+ int result = fseek (m_filePtr, 0, SEEK_SET);
+ if (result)
+ {
+ return true;
+ }
+
+ //
+ // We have the ability to write out the pcap file header in a foreign endian
+ // format, so we need a temp place to swap on the way out.
+ //
+ PcapFileHeader header;
+
+ //
+ // the pointer headerOut selects either the swapped or non-swapped version of
+ // the pcap file header.
+ //
+ PcapFileHeader *headerOut = 0;
+
+ if (m_swapMode == false)
+ {
+ headerOut = &m_fileHeader;
+ }
+ else
+ {
+ Swap (&m_fileHeader, &header);
+ headerOut = &header;
+ }
+
+ //
+ // Watch out for memory alignment differences between machines, so write
+ // them all individually.
+ //
+ result = 0;
+
+ result |= (fwrite (&headerOut->m_magicNumber, sizeof(headerOut->m_magicNumber), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_versionMajor, sizeof(headerOut->m_versionMajor), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_versionMinor, sizeof(headerOut->m_versionMinor), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_zone, sizeof(headerOut->m_zone), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_sigFigs, sizeof(headerOut->m_sigFigs), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_snapLen, sizeof(headerOut->m_snapLen), 1, m_filePtr) != 1);
+ result |= (fwrite (&headerOut->m_type, sizeof(headerOut->m_type), 1, m_filePtr) != 1);
+
+ //
+ // If any of the fwrites above did not succeed in writinging the correct
+ // number of objects, result will be nonzero and will indicate an error.
+ //
+ return result != 0;
+}
+
+bool
+PcapFile::ReadAndVerifyFileHeader (void)
+{
+ //
+ // Pcap file header is always at the start of the file
+ //
+ int result = fseek (m_filePtr, 0, SEEK_SET);
+ if (result)
+ {
+ return true;
+ }
+
+ //
+ // Watch out for memory alignment differences between machines, so read
+ // them all individually.
+ //
+ result = 0;
+
+ result |= (fread (&m_fileHeader.m_magicNumber, sizeof(m_fileHeader.m_magicNumber), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_versionMajor, sizeof(m_fileHeader.m_versionMajor), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_versionMinor, sizeof(m_fileHeader.m_versionMinor), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_zone, sizeof(m_fileHeader.m_zone), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_sigFigs, sizeof(m_fileHeader.m_sigFigs), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_snapLen, sizeof(m_fileHeader.m_snapLen), 1, m_filePtr) != 1);
+ result |= (fread (&m_fileHeader.m_type, sizeof(m_fileHeader.m_type), 1, m_filePtr) != 1);
+
+ //
+ // If any of the freads above did not succeed in reading the correct number of
+ // objects, result will be nonzero.
+ //
+ if (result)
+ {
+ return true;
+ }
+
+ //
+ // There are four possible magic numbers that can be there. Normal and byte
+ // swapped versions of the standard magic number, and normal and byte swapped
+ // versions of the magic number indicating nanosecond resolution timestamps.
+ //
+ if (m_fileHeader.m_magicNumber != MAGIC && m_fileHeader.m_magicNumber != SWAPPED_MAGIC &&
+ m_fileHeader.m_magicNumber != NS_MAGIC && m_fileHeader.m_magicNumber != NS_SWAPPED_MAGIC)
+ {
+ return true;
+ }
+
+ //
+ // If the magic number is swapped, then we can assume that everything else we read
+ // is swapped.
+ //
+ m_swapMode = (m_fileHeader.m_magicNumber == SWAPPED_MAGIC || m_fileHeader.m_magicNumber == NS_SWAPPED_MAGIC) ? true : false;
+
+ if (m_swapMode)
+ {
+ Swap (&m_fileHeader, &m_fileHeader);
+ }
+
+ //
+ // We only deal with one version of the pcap file format.
+ //
+ if (m_fileHeader.m_versionMajor != VERSION_MAJOR || m_fileHeader.m_versionMinor != VERSION_MINOR)
+ {
+ return true;
+ }
+
+ //
+ // A quick test of reasonablness for the time zone offset corresponding to
+ // a real place on the planet.
+ //
+ if (m_fileHeader.m_zone < -12 || m_fileHeader.m_zone > 12)
+ {
+ return true;
+ }
+
+ m_haveFileHeader = true;
+ return false;
+}
+
+bool
+PcapFile::Open (std::string const &filename, std::string const &mode)
+{
+ //
+ // If opening a new file, implicit close of any existing file required.
+ //
+ Close ();
+
+ //
+ // All pcap files are binary files, so we just do this automatically.
+ //
+ std::string realMode = mode + "b";
+
+ //
+ // Our modes may be subtly different from the standard fopen semantics since
+ // we need to have a pcap file header to succeed in some cases; so we need
+ // to process different modes according to our own definitions of the modes.
+ //
+ // In the case of read modes, we must read, check and save the pcap file
+ // header as well as just opening the file.
+ //
+ // In the case of write modes, we just pass the call on through to the
+ // library.
+ //
+ // In the case of append modes, we change the semantics to require the
+ // given file to exist. We can't just create a file since we can't make up
+ // a pcap file header on our own.
+ //
+ if (realMode == "rb" || realMode == "r+b")
+ {
+ m_filePtr = fopen (filename.c_str (), realMode.c_str ());
+ if (m_filePtr == 0)
+ {
+ return true;
+ }
+ m_filename = filename;
+ return ReadAndVerifyFileHeader ();
+ }
+ else if (realMode == "wb" || realMode == "w+b")
+ {
+ m_filePtr = fopen (filename.c_str (), realMode.c_str ());
+ if (m_filePtr)
+ {
+ m_filename = filename;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else if (realMode == "ab" || realMode == "a+b")
+ {
+ //
+ // Remember that semantics for append are different here. We never create
+ // a file since we can't make up a pcap file header. We first have to
+ // open the file in read-only mode and check to see that it exists and
+ // read the file header. If this all works out, then we can go ahead and
+ // open the file in append mode and seek to the end (imlicitly).
+ //
+ m_filePtr = fopen (filename.c_str (), "rb");
+ if (m_filePtr == 0)
+ {
+ return true;
+ }
+
+ bool result = ReadAndVerifyFileHeader ();
+ if (result == true)
+ {
+ Close ();
+ return true;
+ }
+
+ //
+ // We have a properly initialized file and have the pcap file header
+ // loaded and checked. This means that the file meets all of the
+ // critera for opening in append mode, but the file is in read-only mode
+ // now -- we must close it and open it in the correct mode.
+ //
+ fclose (m_filePtr);
+ m_filePtr = 0;
+
+ m_filePtr = fopen (filename.c_str (), realMode.c_str ());
+ if (m_filePtr == 0)
+ {
+ return true;
+ }
+
+ m_filename = filename;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool
+PcapFile::Init (uint32_t dataLinkType, uint32_t snapLen, int32_t timeZoneCorrection, bool swapMode)
+{
+ //
+ // Initialize the in-memory file header.
+ //
+ m_fileHeader.m_magicNumber = MAGIC;
+ m_fileHeader.m_versionMajor = VERSION_MAJOR;
+ m_fileHeader.m_versionMinor = VERSION_MINOR;
+ m_fileHeader.m_zone = timeZoneCorrection;
+ m_fileHeader.m_sigFigs = 0;
+ m_fileHeader.m_snapLen = snapLen;
+ m_fileHeader.m_type = dataLinkType;
+
+ m_haveFileHeader = true;
+ m_swapMode = swapMode;
+
+ return WriteFileHeader ();
+}
+
+bool
+PcapFile::Write (uint32_t tsSec, uint32_t tsUsec, uint8_t const * const data, uint32_t totalLen)
+{
+ if (m_haveFileHeader == false)
+ {
+ return true;
+ }
+
+ uint32_t inclLen = totalLen > m_fileHeader.m_snapLen ? m_fileHeader.m_snapLen : totalLen;
+
+ PcapRecordHeader header;
+ header.m_tsSec = tsSec;
+ header.m_tsUsec = tsUsec;
+ header.m_inclLen = inclLen;
+ header.m_origLen = totalLen;
+
+ if (m_swapMode)
+ {
+ Swap (&header, &header);
+ }
+
+ //
+ // Watch out for memory alignment differences between machines, so write
+ // them all individually.
+ //
+ uint32_t result = 0;
+
+ result |= (fwrite (&header.m_tsSec, sizeof(header.m_tsSec), 1, m_filePtr) != 1);
+ result |= (fwrite (&header.m_tsUsec, sizeof(header.m_tsUsec), 1, m_filePtr) != 1);
+ result |= (fwrite (&header.m_inclLen, sizeof(header.m_inclLen), 1, m_filePtr) != 1);
+ result |= (fwrite (&header.m_origLen, sizeof(header.m_origLen), 1, m_filePtr) != 1);
+
+ result |= fwrite (data, 1, inclLen, m_filePtr) != inclLen;
+
+ return result != 0;
+}
+
+bool
+PcapFile::Read (
+ uint8_t * const data,
+ uint32_t maxBytes,
+ uint32_t &tsSec,
+ uint32_t &tsUsec,
+ uint32_t &inclLen,
+ uint32_t &origLen,
+ uint32_t &readLen)
+{
+ if (m_haveFileHeader == false)
+ {
+ return true;
+ }
+
+ PcapRecordHeader header;
+
+ //
+ // Watch out for memory alignment differences between machines, so read
+ // them all individually.
+ //
+ uint32_t result = 0;
+
+ result |= (fread (&header.m_tsSec, sizeof(header.m_tsSec), 1, m_filePtr) != 1);
+ result |= (fread (&header.m_tsUsec, sizeof(header.m_tsUsec), 1, m_filePtr) != 1);
+ result |= (fread (&header.m_inclLen, sizeof(header.m_inclLen), 1, m_filePtr) != 1);
+ result |= (fread (&header.m_origLen, sizeof(header.m_origLen), 1, m_filePtr) != 1);
+
+ //
+ // If any of the freads above did not succeed in reading the correct number of
+ // objects, result will be nonzero.
+ //
+ if (result)
+ {
+ return true;
+ }
+
+ if (m_swapMode)
+ {
+ Swap (&header, &header);
+ }
+
+ tsSec = header.m_tsSec;
+ tsUsec = header.m_tsUsec;
+ inclLen = header.m_inclLen;
+ origLen = header.m_origLen;
+
+ //
+ // We don't always want to force the client to keep a maximum length buffer
+ // around so we allow her to specify a minimum number of bytes to read.
+ // Usually 64 bytes is enough information to print all of the headers, so
+ // it isn't typically necessary to read all thousand bytes of an echo packet,
+ // for example, to figure out what is going on.
+ //
+ readLen = maxBytes < header.m_inclLen ? maxBytes : header.m_inclLen;
+ result = fread (data, 1, readLen, m_filePtr) != readLen;
+ if (result)
+ {
+ return result;
+ }
+
+ //
+ // To keep the file pointer pointed in the right place, however, we always
+ // need to account for the entire packet as stored originally.
+ //
+ if (readLen < header.m_inclLen)
+ {
+ uint64_t pos = ftell (m_filePtr);
+ int result = fseek (m_filePtr, pos + header.m_inclLen - readLen, SEEK_SET);
+ if (result)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} //namespace ns3
diff --git a/src/common/pcap-file.h b/src/common/pcap-file.h
new file mode 100644
index 000000000..2e9fe03e7
--- /dev/null
+++ b/src/common/pcap-file.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2009 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PCAP_FILE_H
+#define PCAP_FILE_H
+
+#include
+#include
+
+namespace ns3 {
+
+/*
+ * A class representing a pcap file. This allows easy creation, writing and
+ * reading of files composed of stored packets; which may be viewed using
+ * standard tools.
+ */
+
+class PcapFile
+{
+public:
+ static const int32_t ZONE_DEFAULT = 0; /**< Time zone offset for current location */
+ static const uint32_t SNAPLEN_DEFAULT = 65535; /**< Default value for maximum octets to save per packet */
+
+public:
+ PcapFile ();
+ ~PcapFile ();
+
+ /**
+ * Create a new pcap file or open an existing pcap file. Semantics are
+ * similar to the C standard library function \c fopen, but differ in that
+ * positions in the file are based on packets not characters. For example
+ * if the file is opened for reading, the file position indicator (seek
+ * position) points to the beginning of the first packet in the file, not
+ * zero (which would point to the start of the pcap header).
+ *
+ * Possible modes are:
+ *
+ * \verbatim
+ * "r": Open a file for reading. The file must exist. The pcap header
+ * is assumed to exist in the file and will be read and checked.
+ * The file seek position indicator is set to point to the first
+ * packet on exit.
+ *
+ * "w": Create an empty file for writing. If a file with the same name
+ * already exists its content is erased and the file is treated as a
+ * new empty pcap file. The file is assumed not to have a pcap
+ * header and the caller is responsible for calling Init before saving
+ * any packet data. The file seek position indicator is set to point
+ * to the beginning of the file on exit since there will be no pcap
+ * header.
+ *
+ * "a": Append to an existing file. This mode allows for adding packet data
+ * to the end of an existing pcap file. The file must exist and have a
+ * valid pcap header written (N.B. this is different from standard fopen
+ * semantics). The file seek position indicator is set to point
+ * to the end of the file on exit.
+ *
+ * "r+": Open a file for update -- both reading and writing. The file must
+ * exist. The pcap header is assumed to have been written to the
+ * file and will be read and checked. The file seek position indicator
+ * is set to point to the first packet on exit.
+ *
+ * "w+": Create an empty file for both reading and writing. If a file with
+ * the same name already exists, its content is erased and the file is
+ * treated as a new empty pcap file. Since this new file will not have
+ * a pcap header, the caller is responsible for calling Init before
+ * saving any packet data. On exit, the file seek position indicator is
+ * set to point to the beginning of the file.
+ *
+ * "a+" Open a file for reading and appending. The file must exist and have a
+ * valid pcap header written (N.B. this is different from standard fopen
+ * semantics). The file seek position indicator is set to point
+ * to the end of the file on exit. Existing content is preserved.
+ * \endverbatim
+ *
+ * Since a pcap file is always a binary file, the file type is automatically
+ * selected as a binary file. For example, providing a mode string "a+"
+ * results in the underlying OS file being opened in "a+b" mode.
+ *
+ * \param filename String containing the name of the file.
+ *
+ * \param mode String containing the access mode for the file.
+ *
+ * \returns Error indication that should be interpreted as, "did an error
+ * happen"? That is, the method returns false if the open succeeds, true
+ * otherwise. The errno variable will be set by the OS to to provide a
+ * more descriptive failure indication.
+ */
+ bool Open (std::string const &filename, std::string const &mode);
+
+ void Close (void);
+
+ /**
+ * Initialize the pcap file associated with this object. This file must have
+ * been previously opened with write permissions.
+ *
+ * \param dataLinkType A data link type as defined in the pcap library. If
+ * you want to make resulting pcap files visible in existing tools, the
+ * data link type must match existing definitions, such as PCAP_ETHERNET,
+ * PCAP_PPP, PCAP_80211, etc. If you are storing different kinds of packet
+ * data, such as naked TCP headers, you are at liberty to locally define your
+ * own data link types. According to the pcap-linktype man page, "well-known"
+ * pcap linktypes range from 0 to 177. If you use a large random number for
+ * your type, chances are small for a collision.
+ *
+ * \param snapLen An optional maximum size for packets written to the file.
+ * Defaults to 65535. If packets exceed this length they are truncated.
+ *
+ * \param timeZoneCorrection An integer describing the offset of your local
+ * time zone from UTC/GMT. For example, Pacific Standard Time in the US is
+ * GMT-8, so one would enter -8 for that correction. Defaults to 0 (UTC).
+ *
+ * \returns false if the open succeeds, true otherwise.
+ *
+ * \warning Calling this method on an existing file will result in the loss
+ * any existing data.
+ */
+ bool Init (uint32_t dataLinkType,
+ uint32_t snapLen = SNAPLEN_DEFAULT,
+ int32_t timeZoneCorrection = ZONE_DEFAULT,
+ bool swapMode = false);
+
+ bool Write (uint32_t tsSec, uint32_t tsUsec, uint8_t const * const data, uint32_t totalLen);
+
+ bool Read (uint8_t * const data,
+ uint32_t maxBytes,
+ uint32_t &tsSec,
+ uint32_t &tsUsec,
+ uint32_t &inclLen,
+ uint32_t &origLen,
+ uint32_t &readLen);
+
+ bool GetSwapMode (void);
+
+ uint32_t GetMagic (void);
+ uint16_t GetVersionMajor (void);
+ uint16_t GetVersionMinor (void);
+ int32_t GetTimeZoneOffset (void);
+ uint32_t GetSigFigs (void);
+ uint32_t GetSnapLen (void);
+ uint32_t GetDataLinkType (void);
+
+private:
+ typedef struct {
+ uint32_t m_magicNumber; /**< Magic number identifying this as a pcap file */
+ uint16_t m_versionMajor; /**< Major version identifying the version of pcap used in this file */
+ uint16_t m_versionMinor; /**< Minor version identifying the version of pcap used in this file */
+ int32_t m_zone; /**< Time zone correction to be applied to timestamps of packets */
+ uint32_t m_sigFigs; /**< Unused by pretty much everybody */
+ uint32_t m_snapLen; /**< Maximum length of packet data stored in records */
+ uint32_t m_type; /**< Data link type of packet data */
+ } PcapFileHeader;
+
+ typedef struct {
+ uint32_t m_tsSec; /**< seconds part of timestamp */
+ uint32_t m_tsUsec; /**< microseconds part of timestamp (nsecs for PCAP_NSEC_MAGIC) */
+ uint32_t m_inclLen; /**< number of octets of packet saved in file */
+ uint32_t m_origLen; /**< actual length of original packet */
+ } PcapRecordHeader;
+
+ uint8_t Swap (uint8_t val);
+ uint16_t Swap (uint16_t val);
+ uint32_t Swap (uint32_t val);
+ void Swap (PcapFileHeader *from, PcapFileHeader *to);
+ void Swap (PcapRecordHeader *from, PcapRecordHeader *to);
+
+ bool WriteFileHeader (void);
+ bool ReadAndVerifyFileHeader (void);
+
+ std::string m_filename;
+ FILE *m_filePtr;
+ PcapFileHeader m_fileHeader;
+ bool m_haveFileHeader;
+ bool m_swapMode;
+};
+
+}//namespace ns3
+
+#endif /* PCAP_FILE_H */
diff --git a/src/common/wscript b/src/common/wscript
index 2ca057376..077d139d4 100644
--- a/src/common/wscript
+++ b/src/common/wscript
@@ -18,6 +18,8 @@ def build(bld):
'tag-buffer.cc',
'packet-tag-list.cc',
'ascii-writer.cc',
+ 'pcap-file.cc',
+ 'pcap-file-test-suite.cc',
]
headers = bld.new_task_gen('ns3header')
@@ -38,4 +40,5 @@ def build(bld):
'packet-tag-list.h',
'ascii-writer.h',
'sgi-hashmap.h',
+ 'pcap-file.h',
]
diff --git a/src/core/names-test-suite.cc b/src/core/names-test-suite.cc
new file mode 100644
index 000000000..626e9c605
--- /dev/null
+++ b/src/core/names-test-suite.cc
@@ -0,0 +1,975 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include "names.h"
+
+using namespace ns3;
+
+// ===========================================================================
+// Cook up a couple of simple object class that we can use in the object
+// naming tests. They do nothing but be of the right type.
+// ===========================================================================
+class TestObject : public Object
+{
+public:
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("TestObject")
+ .SetParent (Object::GetTypeId ())
+ .HideFromDocumentation ()
+ .AddConstructor ();
+ return tid;
+ }
+ TestObject () {}
+ virtual void Dispose (void) {}
+};
+
+class AlternateTestObject : public Object
+{
+public:
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("AlternateTestObject")
+ .SetParent (Object::GetTypeId ())
+ .HideFromDocumentation ()
+ .AddConstructor ();
+ return tid;
+ }
+ AlternateTestObject () {}
+ virtual void Dispose (void) {}
+};
+
+// ===========================================================================
+// Test case to make sure that the Object Name Service can do its most basic
+// job and add associations between Objects using the lowest level add
+// function, which is:
+//
+// Add (Ptr