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.

    + +

    +
  • + +
  • Wireless Mesh Networking models + +

    +
  • + +
  • 802.11 enhancements +

    +

    +

    +
  • +

    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 = " <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 context, std::string name, Ptr object); +// +// All other add functions will just translate into this form, so this is the +// most basic Add functionality. +// =========================================================================== +class BasicAddTestCase : public TestCase +{ +public: + BasicAddTestCase (); + virtual ~BasicAddTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +BasicAddTestCase::BasicAddTestCase () + : TestCase ("Check low level Names::Add and Names::FindName functionality") +{ +} + +BasicAddTestCase::~BasicAddTestCase () +{ +} + +void +BasicAddTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +BasicAddTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add (Ptr (0, false), "Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add (Ptr (0, false), "Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add (objectOne, "Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add (objectTwo, "Child", childOfObjectTwo); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object"); + + found = Names::FindName (objectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + found = Names::FindName (childOfObjectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can correctly use a +// string context in the most basic ways +// +// Add (std::string context, std::string name, Ptr object); +// +// High level path-based functions will translate into this form, so this is +// the second most basic Add functionality. +// =========================================================================== +class StringContextAddTestCase : public TestCase +{ +public: + StringContextAddTestCase (); + virtual ~StringContextAddTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +StringContextAddTestCase::StringContextAddTestCase () + : TestCase ("Check string context Names::Add and Names::FindName functionality") + +{ +} + +StringContextAddTestCase::~StringContextAddTestCase () +{ +} + +void +StringContextAddTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +StringContextAddTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("/Names", "Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("/Names", "Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name One", "Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("/Names/Name Two", "Child", childOfObjectTwo); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object"); + + found = Names::FindName (objectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + found = Names::FindName (childOfObjectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can correctly use a +// fully qualified path to add assocations +// +// Add (std::string name, Ptr object); +// =========================================================================== +class FullyQualifiedAddTestCase : public TestCase +{ +public: + FullyQualifiedAddTestCase (); + virtual ~FullyQualifiedAddTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +FullyQualifiedAddTestCase::FullyQualifiedAddTestCase () + : TestCase ("Check fully qualified path Names::Add and Names::FindName functionality") + +{ +} + +FullyQualifiedAddTestCase::~FullyQualifiedAddTestCase () +{ +} + +void +FullyQualifiedAddTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +FullyQualifiedAddTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("/Names/Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("/Names/Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("/Names/Name Two/Child", childOfObjectTwo); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object"); + + found = Names::FindName (objectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + found = Names::FindName (childOfObjectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can correctly use a +// relative path to add assocations. This functionality is provided as a +// convenience so clients don't always have to provide the name service +// namespace name in all of their strings. +// +// +// Add (std::string name, Ptr object); +// =========================================================================== +class RelativeAddTestCase : public TestCase +{ +public: + RelativeAddTestCase (); + virtual ~RelativeAddTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +RelativeAddTestCase::RelativeAddTestCase () + : TestCase ("Check relative path Names::Add and Names::FindName functionality") + +{ +} + +RelativeAddTestCase::~RelativeAddTestCase () +{ +} + +void +RelativeAddTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +RelativeAddTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("Name Two/Child", childOfObjectTwo); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name One", "Could not Names::Add and Names::FindName an Object"); + + found = Names::FindName (objectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Name Two", "Could not Names::Add and Names::FindName a second Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + found = Names::FindName (childOfObjectTwo); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can rename objects in +// its most basic way, which is +// +// Rename (Ptr context, std::string oldname, std::string newname); +// +// All other rename functions will just translate into this form, so this is the +// most basic rename functionality. +// =========================================================================== +class BasicRenameTestCase : public TestCase +{ +public: + BasicRenameTestCase (); + virtual ~BasicRenameTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +BasicRenameTestCase::BasicRenameTestCase () + : TestCase ("Check low level Names::Rename functionality") +{ +} + +BasicRenameTestCase::~BasicRenameTestCase () +{ +} + +void +BasicRenameTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +BasicRenameTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add (Ptr (0, false), "Name", objectOne); + + Ptr childOfObjectOne = CreateObject (); + Names::Add (objectOne, "Child", childOfObjectOne); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object"); + + Names::Rename (Ptr (0, false), "Name", "New Name"); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + Names::Rename (objectOne, "Child", "New Child"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can rename objects +// using a string context +// +// Rename (std::string context, std::string oldname, std::string newname); +// =========================================================================== +class StringContextRenameTestCase : public TestCase +{ +public: + StringContextRenameTestCase (); + virtual ~StringContextRenameTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +StringContextRenameTestCase::StringContextRenameTestCase () + : TestCase ("Check string context-based Names::Rename functionality") +{ +} + +StringContextRenameTestCase::~StringContextRenameTestCase () +{ +} + +void +StringContextRenameTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +StringContextRenameTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("/Names", "Name", objectOne); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name", "Child", childOfObjectOne); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object"); + + Names::Rename ("/Names", "Name", "New Name"); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + Names::Rename ("/Names/New Name", "Child", "New Child"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can rename objects +// using a fully qualified path name +// +// Rename (std::string oldpath, std::string newname); +// =========================================================================== +class FullyQualifiedRenameTestCase : public TestCase +{ +public: + FullyQualifiedRenameTestCase (); + virtual ~FullyQualifiedRenameTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +FullyQualifiedRenameTestCase::FullyQualifiedRenameTestCase () + : TestCase ("Check fully qualified path Names::Rename functionality") +{ +} + +FullyQualifiedRenameTestCase::~FullyQualifiedRenameTestCase () +{ +} + +void +FullyQualifiedRenameTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +FullyQualifiedRenameTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("/Names/Name", objectOne); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name/Child", childOfObjectOne); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object"); + + Names::Rename ("/Names/Name", "New Name"); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + Names::Rename ("/Names/New Name/Child", "New Child"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can rename objects +// using a relaltive path name +// +// Rename (std::string oldpath, std::string newname); +// =========================================================================== +class RelativeRenameTestCase : public TestCase +{ +public: + RelativeRenameTestCase (); + virtual ~RelativeRenameTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +RelativeRenameTestCase::RelativeRenameTestCase () + : TestCase ("Check relative path Names::Rename functionality") +{ +} + +RelativeRenameTestCase::~RelativeRenameTestCase () +{ +} + +void +RelativeRenameTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +RelativeRenameTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name", objectOne); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("Name/Child", childOfObjectOne); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Name", "Could not Names::Add and Names::FindName an Object"); + + Names::Rename ("Name", "New Name"); + + found = Names::FindName (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Name", "Could not Names::Rename an Object"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "Child", "Could not Names::Add and Names::FindName a child Object"); + + Names::Rename ("New Name/Child", "New Child"); + + found = Names::FindName (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "New Child", "Could not Names::Rename a child Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can look up an object +// and return its fully qualified path name +// +// FindPath (Ptr object); +// =========================================================================== +class FindPathTestCase : public TestCase +{ +public: + FindPathTestCase (); + virtual ~FindPathTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +FindPathTestCase::FindPathTestCase () + : TestCase ("Check Names::FindPath functionality") +{ +} + +FindPathTestCase::~FindPathTestCase () +{ +} + +void +FindPathTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +FindPathTestCase::DoRun (void) +{ + std::string found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name", objectOne); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name/Child", childOfObjectOne); + + found = Names::FindPath (objectOne); + NS_TEST_ASSERT_MSG_EQ (found, "/Names/Name", "Could not Names::Add and Names::FindPath an Object"); + + found = Names::FindPath (childOfObjectOne); + NS_TEST_ASSERT_MSG_EQ (found, "/Names/Name/Child", "Could not Names::Add and Names::FindPath a child Object"); + + Ptr objectNotThere = CreateObject (); + found = Names::FindPath (objectNotThere); + NS_TEST_ASSERT_MSG_EQ (found, "", "Unexpectedly found a non-existent Object"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can find Objects using +// the lowest level find function, which is: +// +// Find (Ptr context, std::string name); +// =========================================================================== +class BasicFindTestCase : public TestCase +{ +public: + BasicFindTestCase (); + virtual ~BasicFindTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +BasicFindTestCase::BasicFindTestCase () + : TestCase ("Check low level Names::Find functionality") +{ +} + +BasicFindTestCase::~BasicFindTestCase () +{ +} + +void +BasicFindTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +BasicFindTestCase::DoRun (void) +{ + Ptr found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("Name Two/Child", childOfObjectTwo); + + found = Names::Find (Ptr (0, false), "Name One"); + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via object context"); + + found = Names::Find (Ptr (0, false), "Name Two"); + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via object context"); + + found = Names::Find (objectOne, "Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via object context"); + + found = Names::Find (objectTwo, "Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via object context"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can find Objects using +// a string context-based find function, which is: +// +// Find (std::string context, std::string name); +// =========================================================================== +class StringContextFindTestCase : public TestCase +{ +public: + StringContextFindTestCase (); + virtual ~StringContextFindTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +StringContextFindTestCase::StringContextFindTestCase () + : TestCase ("Check string context-based Names::Find functionality") +{ +} + +StringContextFindTestCase::~StringContextFindTestCase () +{ +} + +void +StringContextFindTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +StringContextFindTestCase::DoRun (void) +{ + Ptr found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("Name Two/Child", childOfObjectTwo); + + found = Names::Find ("/Names", "Name One"); + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context"); + + found = Names::Find ("/Names", "Name Two"); + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context"); + + found = Names::Find ("/Names/Name One", "Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context"); + + found = Names::Find ("/Names/Name Two", "Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via string context"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can find Objects using +// a fully qualified path name-based find function, which is: +// +// Find (std::string name); +// =========================================================================== +class FullyQualifiedFindTestCase : public TestCase +{ +public: + FullyQualifiedFindTestCase (); + virtual ~FullyQualifiedFindTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +FullyQualifiedFindTestCase::FullyQualifiedFindTestCase () + : TestCase ("Check fully qualified path Names::Find functionality") +{ +} + +FullyQualifiedFindTestCase::~FullyQualifiedFindTestCase () +{ +} + +void +FullyQualifiedFindTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +FullyQualifiedFindTestCase::DoRun (void) +{ + Ptr found; + + Ptr objectOne = CreateObject (); + Names::Add ("/Names/Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("/Names/Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("/Names/Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("/Names/Name Two/Child", childOfObjectTwo); + + found = Names::Find ("/Names/Name One"); + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context"); + + found = Names::Find ("/Names/Name Two"); + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context"); + + found = Names::Find ("/Names/Name One/Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context"); + + found = Names::Find ("/Names/Name Two/Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via string context"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can find Objects using +// a relative path name-based find function, which is: +// +// Find (std::string name); +// =========================================================================== +class RelativeFindTestCase : public TestCase +{ +public: + RelativeFindTestCase (); + virtual ~RelativeFindTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +RelativeFindTestCase::RelativeFindTestCase () + : TestCase ("Check relative path Names::Find functionality") +{ +} + +RelativeFindTestCase::~RelativeFindTestCase () +{ +} + +void +RelativeFindTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +RelativeFindTestCase::DoRun (void) +{ + Ptr found; + + Ptr objectOne = CreateObject (); + Names::Add ("Name One", objectOne); + + Ptr objectTwo = CreateObject (); + Names::Add ("Name Two", objectTwo); + + Ptr childOfObjectOne = CreateObject (); + Names::Add ("Name One/Child", childOfObjectOne); + + Ptr childOfObjectTwo = CreateObject (); + Names::Add ("Name Two/Child", childOfObjectTwo); + + found = Names::Find ("Name One"); + NS_TEST_ASSERT_MSG_EQ (found, objectOne, "Could not find a previously named Object via string context"); + + found = Names::Find ("Name Two"); + NS_TEST_ASSERT_MSG_EQ (found, objectTwo, "Could not find a previously named Object via stribng context"); + + found = Names::Find ("Name One/Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectOne, "Could not find a previously named child Object via string context"); + + found = Names::Find ("Name Two/Child"); + NS_TEST_ASSERT_MSG_EQ (found, childOfObjectTwo, "Could not find a previously named child Object via string context"); + + return false; +} + +// =========================================================================== +// Test case to make sure that the Object Name Service can find Objects using +// a second type. +// =========================================================================== +class AlternateFindTestCase : public TestCase +{ +public: + AlternateFindTestCase (); + virtual ~AlternateFindTestCase (); + +private: + virtual bool DoRun (void); + virtual void DoTeardown (void); +}; + +AlternateFindTestCase::AlternateFindTestCase () + : TestCase ("Check GetObject operation in Names::Find") +{ +} + +AlternateFindTestCase::~AlternateFindTestCase () +{ +} + +void +AlternateFindTestCase::DoTeardown (void) +{ + Names::Delete (); +} + +bool +AlternateFindTestCase::DoRun (void) +{ + Ptr testObject = CreateObject (); + Names::Add ("Test Object", testObject); + + Ptr alternateTestObject = CreateObject (); + Names::Add ("Alternate Test Object", alternateTestObject); + + Ptr foundTestObject; + Ptr foundAlternateTestObject; + + foundTestObject = Names::Find ("Test Object"); + NS_TEST_ASSERT_MSG_EQ (foundTestObject, testObject, + "Could not find a previously named TestObject via GetObject"); + + foundAlternateTestObject = Names::Find ("Alternate Test Object"); + NS_TEST_ASSERT_MSG_EQ (foundAlternateTestObject, alternateTestObject, + "Could not find a previously named AlternateTestObject via GetObject"); + + + foundAlternateTestObject = Names::Find ("Test Object"); + NS_TEST_ASSERT_MSG_EQ (foundAlternateTestObject, 0, + "Unexpectedly able to GetObject on a TestObject"); + + foundTestObject = Names::Find ("Alternate Test Object"); + NS_TEST_ASSERT_MSG_EQ (foundTestObject, 0, + "Unexpectedly able to GetObject on an AlternateTestObject"); + + return false; +} + +class NamesTestSuite : public TestSuite +{ +public: + NamesTestSuite (); +}; + +NamesTestSuite::NamesTestSuite () + : TestSuite ("object-name-service", UNIT) +{ + AddTestCase (new BasicAddTestCase); + AddTestCase (new StringContextAddTestCase); + AddTestCase (new FullyQualifiedAddTestCase); + AddTestCase (new RelativeAddTestCase); + AddTestCase (new BasicRenameTestCase); + AddTestCase (new StringContextRenameTestCase); + AddTestCase (new FullyQualifiedRenameTestCase); + AddTestCase (new RelativeRenameTestCase); + AddTestCase (new FindPathTestCase); + AddTestCase (new BasicFindTestCase); + AddTestCase (new StringContextFindTestCase); + AddTestCase (new FullyQualifiedFindTestCase); + AddTestCase (new RelativeFindTestCase); + AddTestCase (new AlternateFindTestCase); +} + +NamesTestSuite namesTestSuite; diff --git a/src/core/rng-test-suite.cc b/src/core/rng-test-suite.cc new file mode 100644 index 000000000..49f20a68b --- /dev/null +++ b/src/core/rng-test-suite.cc @@ -0,0 +1,434 @@ +/* -*- 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 + +#include "test.h" +#include "random-variable.h" + +using namespace ns3; + +void +FillHistoRangeUniformly (double *array, uint32_t n, double start, double end) +{ + double increment = (end - start) / (n - 1.); + double d = start; + + for (uint32_t i = 0; i < n; ++i) + { + array[i] = d; + d += increment; + } +} + +// =========================================================================== +// Test case for uniform distribution random number generator +// =========================================================================== +class RngUniformTestCase : public TestCase +{ +public: + static const uint32_t N_RUNS = 5; + static const uint32_t N_BINS = 50; + static const uint32_t N_MEASUREMENTS = 1000000; + + RngUniformTestCase (); + virtual ~RngUniformTestCase (); + + double ChiSquaredTest (UniformVariable &u); + +private: + virtual bool DoRun (void); +}; + +RngUniformTestCase::RngUniformTestCase () + : TestCase ("Uniform Random Number Generator") +{ +} + +RngUniformTestCase::~RngUniformTestCase () +{ +} + +double +RngUniformTestCase::ChiSquaredTest (UniformVariable &u) +{ + gsl_histogram * h = gsl_histogram_alloc (N_BINS); + gsl_histogram_set_ranges_uniform (h, 0., 1.); + + for (uint32_t i = 0; i < N_MEASUREMENTS; ++i) + { + gsl_histogram_increment (h, u.GetValue ()); + } + + double tmp[N_BINS]; + + double expected = ((double)N_MEASUREMENTS / (double)N_BINS); + + for (uint32_t i = 0; i < N_BINS; ++i) + { + tmp[i] = gsl_histogram_get (h, i); + tmp[i] -= expected; + tmp[i] *= tmp[i]; + tmp[i] /= expected; + } + + gsl_histogram_free (h); + + double chiSquared = 0; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + chiSquared += tmp[i]; + } + + return chiSquared; +} + +bool +RngUniformTestCase::DoRun (void) +{ + SeedManager::SetSeed (time (0)); + + double sum = 0.; + double maxStatistic = gsl_cdf_chisq_Qinv (0.05, N_BINS); + + for (uint32_t i = 0; i < N_RUNS; ++i) + { + UniformVariable u; + double result = ChiSquaredTest (u); + sum += result; + } + + sum /= (double)N_RUNS; + + NS_TEST_ASSERT_MSG_LT (sum, maxStatistic, "Chi-squared statistic out of range"); + return false; +} + +// =========================================================================== +// Test case for normal distribution random number generator +// =========================================================================== +class RngNormalTestCase : public TestCase +{ +public: + static const uint32_t N_RUNS = 5; + static const uint32_t N_BINS = 50; + static const uint32_t N_MEASUREMENTS = 1000000; + + RngNormalTestCase (); + virtual ~RngNormalTestCase (); + + double ChiSquaredTest (NormalVariable &n); + +private: + virtual bool DoRun (void); +}; + +RngNormalTestCase::RngNormalTestCase () + : TestCase ("Normal Random Number Generator") +{ +} + +RngNormalTestCase::~RngNormalTestCase () +{ +} + +double +RngNormalTestCase::ChiSquaredTest (NormalVariable &n) +{ + gsl_histogram * h = gsl_histogram_alloc (N_BINS); + + double range[N_BINS + 1]; + FillHistoRangeUniformly (range, N_BINS + 1, -4., 4.); + range[0] = -std::numeric_limits::max (); + range[N_BINS] = std::numeric_limits::max (); + + gsl_histogram_set_ranges (h, range, N_BINS + 1); + + double expected[N_BINS]; + + double sigma = 1.; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + expected[i] = gsl_cdf_gaussian_P (range[i + 1], sigma) - gsl_cdf_gaussian_P (range[i], sigma); + expected[i] *= N_MEASUREMENTS; + } + + for (uint32_t i = 0; i < N_MEASUREMENTS; ++i) + { + gsl_histogram_increment (h, n.GetValue ()); + } + + double tmp[N_BINS]; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + tmp[i] = gsl_histogram_get (h, i); + tmp[i] -= expected[i]; + tmp[i] *= tmp[i]; + tmp[i] /= expected[i]; + } + + gsl_histogram_free (h); + + double chiSquared = 0; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + chiSquared += tmp[i]; + } + + return chiSquared; +} + +bool +RngNormalTestCase::DoRun (void) +{ + SeedManager::SetSeed (time (0)); + + double sum = 0.; + double maxStatistic = gsl_cdf_chisq_Qinv (0.05, N_BINS); + + for (uint32_t i = 0; i < N_RUNS; ++i) + { + NormalVariable n; + double result = ChiSquaredTest (n); + sum += result; + } + + sum /= (double)N_RUNS; + + NS_TEST_ASSERT_MSG_LT (sum, maxStatistic, "Chi-squared statistic out of range"); + return false; +} + +// =========================================================================== +// Test case for exponential distribution random number generator +// =========================================================================== +class RngExponentialTestCase : public TestCase +{ +public: + static const uint32_t N_RUNS = 5; + static const uint32_t N_BINS = 50; + static const uint32_t N_MEASUREMENTS = 1000000; + + RngExponentialTestCase (); + virtual ~RngExponentialTestCase (); + + double ChiSquaredTest (ExponentialVariable &n); + +private: + virtual bool DoRun (void); +}; + +RngExponentialTestCase::RngExponentialTestCase () + : TestCase ("Exponential Random Number Generator") +{ +} + +RngExponentialTestCase::~RngExponentialTestCase () +{ +} + +double +RngExponentialTestCase::ChiSquaredTest (ExponentialVariable &e) +{ + gsl_histogram * h = gsl_histogram_alloc (N_BINS); + + double range[N_BINS + 1]; + FillHistoRangeUniformly (range, N_BINS + 1, 0., 10.); + range[N_BINS] = std::numeric_limits::max (); + + gsl_histogram_set_ranges (h, range, N_BINS + 1); + + double expected[N_BINS]; + + double mu = 1.; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + expected[i] = gsl_cdf_exponential_P (range[i + 1], mu) - gsl_cdf_exponential_P (range[i], mu); + expected[i] *= N_MEASUREMENTS; + } + + for (uint32_t i = 0; i < N_MEASUREMENTS; ++i) + { + gsl_histogram_increment (h, e.GetValue ()); + } + + double tmp[N_BINS]; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + tmp[i] = gsl_histogram_get (h, i); + tmp[i] -= expected[i]; + tmp[i] *= tmp[i]; + tmp[i] /= expected[i]; + } + + gsl_histogram_free (h); + + double chiSquared = 0; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + chiSquared += tmp[i]; + } + + return chiSquared; +} + +bool +RngExponentialTestCase::DoRun (void) +{ + SeedManager::SetSeed (time (0)); + + double sum = 0.; + double maxStatistic = gsl_cdf_chisq_Qinv (0.05, N_BINS); + + for (uint32_t i = 0; i < N_RUNS; ++i) + { + ExponentialVariable e; + double result = ChiSquaredTest (e); + sum += result; + } + + sum /= (double)N_RUNS; + + NS_TEST_ASSERT_MSG_LT (sum, maxStatistic, "Chi-squared statistic out of range"); + return false; +} + +// =========================================================================== +// Test case for pareto distribution random number generator +// =========================================================================== +class RngParetoTestCase : public TestCase +{ +public: + static const uint32_t N_RUNS = 5; + static const uint32_t N_BINS = 50; + static const uint32_t N_MEASUREMENTS = 1000000; + + RngParetoTestCase (); + virtual ~RngParetoTestCase (); + + double ChiSquaredTest (ParetoVariable &p); + +private: + virtual bool DoRun (void); +}; + +RngParetoTestCase::RngParetoTestCase () + : TestCase ("Pareto Random Number Generator") +{ +} + +RngParetoTestCase::~RngParetoTestCase () +{ +} + +double +RngParetoTestCase::ChiSquaredTest (ParetoVariable &p) +{ + gsl_histogram * h = gsl_histogram_alloc (N_BINS); + + double range[N_BINS + 1]; + FillHistoRangeUniformly (range, N_BINS + 1, 1., 10.); + range[N_BINS] = std::numeric_limits::max (); + + gsl_histogram_set_ranges (h, range, N_BINS + 1); + + double expected[N_BINS]; + + double a = 1.5; + double b = 0.33333333; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + expected[i] = gsl_cdf_pareto_P (range[i + 1], a, b) - gsl_cdf_pareto_P (range[i], a, b); + expected[i] *= N_MEASUREMENTS; + } + + for (uint32_t i = 0; i < N_MEASUREMENTS; ++i) + { + gsl_histogram_increment (h, p.GetValue ()); + } + + double tmp[N_BINS]; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + tmp[i] = gsl_histogram_get (h, i); + tmp[i] -= expected[i]; + tmp[i] *= tmp[i]; + tmp[i] /= expected[i]; + } + + gsl_histogram_free (h); + + double chiSquared = 0; + + for (uint32_t i = 0; i < N_BINS; ++i) + { + chiSquared += tmp[i]; + } + + return chiSquared; +} + +bool +RngParetoTestCase::DoRun (void) +{ + SeedManager::SetSeed (time (0)); + + double sum = 0.; + double maxStatistic = gsl_cdf_chisq_Qinv (0.05, N_BINS); + + for (uint32_t i = 0; i < N_RUNS; ++i) + { + ParetoVariable e; + double result = ChiSquaredTest (e); + sum += result; + } + + sum /= (double)N_RUNS; + + NS_TEST_ASSERT_MSG_LT (sum, maxStatistic, "Chi-squared statistic out of range"); + return false; +} + +class RngTestSuite : public TestSuite +{ +public: + RngTestSuite (); +}; + +RngTestSuite::RngTestSuite () + : TestSuite ("random-number-generators", UNIT) +{ + AddTestCase (new RngUniformTestCase); + AddTestCase (new RngNormalTestCase); + AddTestCase (new RngExponentialTestCase); + AddTestCase (new RngParetoTestCase); +} + +RngTestSuite rngTestSuite; diff --git a/src/core/test.cc b/src/core/test.cc index 165827a41..0e53373e4 100644 --- a/src/core/test.cc +++ b/src/core/test.cc @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2005 INRIA + * 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 @@ -14,14 +14,530 @@ * 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: Mathieu Lacage */ #include "test.h" +#include "abort.h" +#include + +namespace ns3 { + +bool +TestDoubleIsEqual (const double x1, const double x2, const double epsilon) +{ + int exponent; + double delta, difference; + + // + // Find exponent of largest absolute value + // + { + double max = (fabs (x1) > fabs (x2)) ? x1 : x2; + frexp (max, &exponent); + } + + // + // Form a neighborhood of size 2 * delta + // + delta = ldexp (epsilon, exponent); + difference = x1 - x2; + + if (difference > delta || difference < -delta) + { + return false; + } + return true; +} + + +TestCase::TestCase (std::string name) + : m_name (name), m_verbose (false), m_basedir ("invalid"), m_ofs (0), m_error (false) +{ +} + +TestCase::~TestCase () +{ +} + +void +TestCase::ReportStart (void) +{ + DoReportStart (); +} + +void +TestCase::ReportSuccess (void) +{ + DoReportSuccess (); +} + +void +TestCase::ReportFailure ( + std::string cond, + std::string actual, + std::string limit, + std::string message, + std::string file, + int32_t line) +{ + DoReportFailure (cond, actual, limit, message, file, line); +} + +void +TestCase::ReportEnd (void) +{ + DoReportStart (); +} + +bool +TestCase::Run (void) +{ + DoReportStart (); + DoSetup (); + m_error |= DoRun (); + DoTeardown (); + if (m_error == false) + { + DoReportSuccess (); + } + DoReportEnd (); + return m_error; +} + +void +TestCase::SetVerbose (bool verbose) +{ + m_verbose = verbose; +} + +void +TestCase::SetName (std::string name) +{ + m_name = name; +} + +std::string +TestCase::GetName (void) +{ + return m_name; +} + +void +TestCase::SetBaseDir (std::string basedir) +{ + m_basedir = basedir; +} + +std::string +TestCase::GetBaseDir (void) +{ + return m_basedir; +} + +std::string +TestCase::GetSourceDir (std::string file) +{ + std::string::size_type relPathBegin = file.find_first_of ("/"); + NS_ABORT_MSG_IF (relPathBegin == std::string::npos, "TestCase::GetSrouceDir(): Internal Error"); + std::string::size_type relPathEnd = file.find_last_of ("/"); + NS_ABORT_MSG_IF (relPathEnd == std::string::npos, "TestCase::GetSrouceDir(): Internal Error"); + + return GetBaseDir () + file.substr (relPathBegin, relPathEnd + 1 - relPathBegin); +} + +void +TestCase::SetStream (std::ofstream *ofs) +{ + m_ofs = ofs; +} + +std::ofstream * +TestCase::GetStream (void) +{ + return m_ofs; +} + +void +TestCase::SetErrorStatus (bool error) +{ + m_error = error; +} + +bool +TestCase::GetErrorStatus (void) +{ + return m_error; +} + + +void +TestCase::DoReportStart (void) +{ + m_startTime = times (&m_startTimes); + + if (m_ofs == 0) + { + return; + } + *m_ofs << " " << std::endl; + *m_ofs << " " << GetName () << "" << std::endl; +} + +void +TestCase::DoReportSuccess (void) +{ + if (m_ofs == 0) + { + return; + } + *m_ofs << " PASS" << std::endl; +} + +void +TestCase::DoReportFailure ( + std::string cond, + std::string actual, + std::string limit, + std::string message, + std::string file, + int32_t line) +{ + if (m_ofs == 0) + { + return; + } + *m_ofs << " FAIL" << std::endl; + *m_ofs << " " << cond << "" << std::endl; + *m_ofs << " " << actual << "" << std::endl; + *m_ofs << " " << limit << "" << std::endl; + *m_ofs << " " << message << "" << std::endl; + *m_ofs << " " << file << "" << std::endl; + *m_ofs << " " << line << "" << std::endl; + m_error |= true; +} + +void +TestCase::DoReportEnd (void) +{ + static long ticksPerSecond = sysconf (_SC_CLK_TCK); + + if (m_ofs == 0) + { + return; + } + + struct tms endTimes; + clock_t endTime = times (&endTimes); + + clock_t elapsed = endTime - m_startTime; + clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; + clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; + + (*m_ofs).precision (2); + *m_ofs << std::fixed; + + *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond + << " user " << static_cast (elapsedUsr) / ticksPerSecond + << " system " << static_cast (elapsedSys) / ticksPerSecond + << "" << std::endl; + + *m_ofs << " " << std::endl; +} + +void +TestCase::DoSetup (void) +{ +} + +void +TestCase::DoTeardown (void) +{ +} + +TestSuite::TestSuite (std::string name, TestType type) + : m_name (name), m_verbose (false), m_basedir ("invalid"), m_ofs (0), m_type (type) +{ + TestRunner::AddTestSuite (this); +} + +TestSuite::~TestSuite () +{ + for (TestCaseVector_t::iterator i = m_tests.begin (); i != m_tests.end (); ++i) + { + delete *i; + *i = 0; + } + + m_tests.erase (m_tests.begin (), m_tests.end ()); +} + +void +TestSuite::ReportStart (void) +{ + DoReportStart (); +} + +void +TestSuite::ReportSuccess (void) +{ + DoReportSuccess (); +} + +void +TestSuite::ReportFailure (void) +{ + DoReportFailure (); +} + +void +TestSuite::ReportEnd (void) +{ + DoReportEnd (); +} + +bool +TestSuite::Run (void) +{ + DoReportStart (); + DoSetup (); + bool error = DoRun (); + DoTeardown (); + if (error == false) + { + DoReportSuccess (); + } + else + { + DoReportFailure (); + } + + DoReportEnd (); + return error; +} + +uint32_t +TestSuite::AddTestCase (TestCase *testCase) +{ + uint32_t index = m_tests.size (); + m_tests.push_back (testCase); + return index; +} + +uint32_t +TestSuite::GetNTestCases (void) +{ + return m_tests.size (); +} + +TestCase * +TestSuite::GetTestCase (uint32_t n) +{ + return m_tests[n]; +} + +TestSuite::TestType +TestSuite::GetTestType (void) +{ + return m_type; +} + +void +TestSuite::SetVerbose (bool verbose) +{ + m_verbose = verbose; +} + +void +TestSuite::SetName (std::string name) +{ + m_name = name; +} + +std::string +TestSuite::GetName (void) +{ + return m_name; +} + +void +TestSuite::SetBaseDir (std::string basedir) +{ + m_basedir = basedir; +} + +std::string +TestSuite::GetBaseDir (void) +{ + return m_basedir; +} + +void +TestSuite::SetStream (std::ofstream *ofs) +{ + m_ofs = ofs; +} + +void +TestSuite::DoReportStart (void) +{ + m_startTime = times (&m_startTimes); + + if (m_ofs == 0) + { + return; + } + *m_ofs << "" << std::endl; + *m_ofs << " " << GetName () << "" << std::endl; +} + +void +TestSuite::DoReportFailure (void) +{ + if (m_ofs == 0) + { + return; + } + *m_ofs << " FAIL" << std::endl; +} + +void +TestSuite::DoReportSuccess (void) +{ + if (m_ofs == 0) + { + return; + } + *m_ofs << " PASS" << std::endl; +} + +void +TestSuite::DoReportEnd (void) +{ + static long ticksPerSecond = sysconf (_SC_CLK_TCK); + + if (m_ofs == 0) + { + return; + } + struct tms endTimes; + clock_t endTime = times (&endTimes); + + clock_t elapsed = endTime - m_startTime; + clock_t elapsedUsr = endTimes.tms_utime - m_startTimes.tms_utime; + clock_t elapsedSys = endTimes.tms_stime - m_startTimes.tms_stime; + + (*m_ofs).precision (2); + *m_ofs << std::fixed; + + *m_ofs << " " << "real " << static_cast (elapsed) / ticksPerSecond + << " user " << static_cast (elapsedUsr) / ticksPerSecond + << " system " << static_cast (elapsedSys) / ticksPerSecond + << "" << std::endl; + + *m_ofs << "" << std::endl; +} + +void +TestSuite::DoSetup (void) +{ +} + +bool +TestSuite::DoRun (void) +{ + for (TestCaseVector_t::iterator i = m_tests.begin (); i != m_tests.end (); ++i) + { + (*i)->SetVerbose (m_verbose); + (*i)->SetBaseDir (m_basedir); + (*i)->SetStream (m_ofs); + bool err = (*i)->Run (); + if (err) + { + return err; + } + } + + return false; +} + +void +TestSuite::DoTeardown (void) +{ +} + +class TestRunnerImpl +{ +public: + uint32_t AddTestSuite (TestSuite *testSuite); + uint32_t GetNTestSuites (void); + TestSuite *GetTestSuite (uint32_t n); + bool RunTestSuite (uint32_t n); + + static TestRunnerImpl *Instance (void); +private: + TestRunnerImpl (); + ~TestRunnerImpl (); + + typedef std::vector TestSuiteVector_t; + TestSuiteVector_t m_suites; +}; + +TestRunnerImpl::TestRunnerImpl () +{ +} + +TestRunnerImpl::~TestRunnerImpl () +{ +} + +TestRunnerImpl * +TestRunnerImpl::Instance (void) +{ + static TestRunnerImpl runner; + return &runner; +} + +uint32_t +TestRunnerImpl::AddTestSuite (TestSuite *testSuite) +{ + uint32_t index = m_suites.size (); + m_suites.push_back (testSuite); + return index; +} + +uint32_t +TestRunnerImpl::GetNTestSuites (void) +{ + return m_suites.size (); +} + +TestSuite * +TestRunnerImpl::GetTestSuite (uint32_t n) +{ + return m_suites[n]; +} + +uint32_t +TestRunner::AddTestSuite (TestSuite *testSuite) +{ + return TestRunnerImpl::Instance ()->AddTestSuite (testSuite); +} + +uint32_t +TestRunner::GetNTestSuites (void) +{ + return TestRunnerImpl::Instance ()->GetNTestSuites (); +} + +TestSuite * +TestRunner::GetTestSuite (uint32_t n) +{ + return TestRunnerImpl::Instance ()->GetTestSuite (n); +} + +}; // namespace ns3 #ifdef RUN_SELF_TESTS -#include namespace ns3 { @@ -66,7 +582,6 @@ TestManager::PrintTestNames (std::ostream &os) } } - std::ostream & TestManager::Failure (void) { diff --git a/src/core/test.h b/src/core/test.h index 26caeae9c..31d720209 100644 --- a/src/core/test.h +++ b/src/core/test.h @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2005 INRIA + * 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 @@ -14,18 +14,986 @@ * 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: Mathieu Lacage */ #ifndef TEST_H #define TEST_H -#include +#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include +// +// Note on below macros: +// +// When multiple statements are used in a macro, they should be bound together +// in a loop syntactically, so the macro can appear safely inside if clauses +// or other places that expect a single statement or a statement block. The +// "strange" do while construct is a generally expected best practice for +// defining a robust macro. +// +/** + * \brief Convenience macro to extract the source directory of the current + * source file. + * + * \see TestCase::GetSourceDir + */ +#define NS_TEST_SOURCEDIR \ + TestCase::GetSourceDir (__FILE__) + +// =========================================================================== +// Test for equality (generic version) +// =========================================================================== + +/** + * \internal + */ +#define NS_TEST_ASSERT_MSG_EQ_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) == (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) EQ " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + return true; \ + } \ + } while (false) + +/** + * \brief Test that an actual and expected (limit) value are equal and report + * and abort if not. + * + * Check to see if the expected (limit) value is equal to the actual value found + * in a test case. If the two values are equal nothing happens, but if the + * comparison fails, an error is reported in a consistent way and the execution + * of the current test case is aborted. + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_ASSERT_MSG_EQ (result, true, + * "cannot open file " << filename << " in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param msg Message that is output if the test does not pass. + * + * \warning Do not use this macro if you are comparing floating point numbers + * (float or double) as it is unlikely to do what you expect. Use + * NS_TEST_ASSERT_MSG_EQ_TOL instead. + */ +#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg) \ + NS_TEST_ASSERT_MSG_EQ_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +/** + * \internal + * + * Required to avoid use of return statement which allows use in methods + * (esp. callbacks) returning void. + */ +#define NS_TEST_EXPECT_MSG_EQ_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) == (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) EQ " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + } \ + } while (false) + +/** + * \brief Test that an actual and expected (limit) value are equal and report + * if not. + * + * Check to see if the expected (lmit) value is equal to the actual value found + * in a test case. If the two values are equal nothing happens, but if the + * comparison fails, an error is reported in a consistent way. EXPECT* macros + * do not return if an error is detected. + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_EXPECT_MSG_EQUAL (result, true, + * "cannot open file " << filename << " in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param msg Message that is output if the test does not pass. + * + * \warning Do not use this macro if you are comparing floating point numbers + * (float or double) as it is unlikely to do what you expect. Use + * NS_TEST_EXPECT_MSG_EQ_TOL instead. + */ +#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg) \ + NS_TEST_EXPECT_MSG_EQ_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +// =========================================================================== +// Test for equality with a provided tolerance (use for floating point +// comparisons -- both float and double) +// =========================================================================== + +/** + * \internal + */ +#define NS_TEST_ASSERT_MSG_EQ_TOL_INTERNAL(actual, limit, tol, msg, file, line) \ + do { \ + if ((actual) > (limit) + (tol) || (actual) < (limit) - (tol)) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit << " +- " << tol; \ + std::ostringstream condStream; \ + condStream << #actual << " (actual) LT " << #limit << " (limit) + " << #tol << " (tol) AND " << \ + #actual << " (actual) GT " << #limit << " (limit) - " << #tol << " (tol)"; \ + ReportFailure (condStream.str (), actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + return true; \ + } \ + } while (false) + +/** + * \brief Test that actual and expected (limit) values are equal to plus or minus + * some tolerance and report and abort if not. + * + * Check to see if the expected (limit) value is equal to the actual value found + * in a test case to some tolerance. This is not the same thing as asking if + * two floating point are equal to within some epsilon, but is useful for that + * case. This assertion is geared toward more of a measurement problem. Consider + * measuring a physical rod of some kind that you have ordered. You need to + * determine if it is "good." You won't measure the rod to an arbitrary precision + * of sixteen significant figures, you will measure the rod to determine if its + * length is within the tolerances you provided. For example, 12.00 inches plus + * or minus .005 inch may be just fine. + * + * In ns-3, you might want to measure a signal to noise ratio and check to see + * if the answer is what you expect. If you naively measure (double)1128.93 and + * compare this number with a constant 1128.93 you are almost certainly going to + * have your test fail because of floating point rounding errors. We provide a + * floating point comparison function ns3::TestDoubleIsEqual() but you will + * probably quickly find that is not what you want either. It may turn out to + * be the case that when you measured an snr that printed as 1128.93, what was + * actually measured was something more like 1128.9287653857625442 for example. + * Given that the double epsilon is on the order of 0.0000000000000009, you would + * need to provide sixteen significant figures of expected value for this kind of + * test to pass even with a typical test for floating point "approximate equality." + * That is clearly not required or desired. You really want to be able to provide + * 1128.93 along with a tolerance just like you provided 12 inches +- 0.005 inch + * above. + * + * This assertion is designed for real measurements by taking into account + * measurement tolerances. By doing so it also automatically compensates for + * floating point rounding errors. If you really want to check floating point + * equality down to the numeric_limits::epsilon () range, consider using + * ns3::TestDoubleIsEqual(). + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_ASSERT_MSG_EQ_TOL (snr, 1128.93, 0.005, "wrong snr (" << snr << ") in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param tol Tolerance of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_ASSERT_MSG_EQ_TOL(actual, limit, tol, msg) \ + NS_TEST_ASSERT_MSG_EQ_TOL_INTERNAL (actual, limit, tol, msg, __FILE__, __LINE__) + +/** + * \internal + * + * Required to avoid use of return statement which allows use in methods + * (esp. callbacks) returning void. + */ +#define NS_TEST_EXPECT_MSG_EQ_TOL_INTERNAL(actual, limit, tol, msg, file, line) \ + do { \ + if ((actual) > (limit) + (tol) || (actual) < (limit) - (tol)) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit << " +- " << tol; \ + std::ostringstream condStream; \ + condStream << #actual << " (actual) LT " << #limit << " (limit) + " << #tol << " (tol) AND " << \ + #actual << " (actual) GT " << #limit << " (limit) - " << #tol << " (tol)"; \ + ReportFailure (condStream.str (), actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + } \ + } while (false) + +/** + * \brief Test that actual and expected (limit) values are equal to plus or minus + * some tolerance and report if not. + * + * Check to see if the expected (limit) value is equal to the actual value found + * in a test case to some tolerance. This is not the same thing as asking if + * two floating point are equal to within some epsilon, but is useful for that + * case. This assertion is geared toward more of a measurement problem. Consider + * measuring a physical rod of some kind that you have ordered. You need to + * determine if it is "good." You won't measure the rod to an arbitrary precision + * of sixteen significant figures, you will measure the rod to determine if its + * length is within the tolerances you provided. For example, 12.00 inches plus + * or minus .005 inch may be just fine. + * + * In ns-3, you might want to measure a signal to noise ratio and check to see + * if the answer is what you expect. If you naively measure (double)1128.93 and + * compare this number with a constant 1128.93 you are almost certainly going to + * have your test fail because of floating point rounding errors. We provide a + * floating point comparison function ns3::TestDoubleIsEqual() but you will + * probably quickly find that is not what you want either. It may turn out to + * be the case that when you measured an snr that printed as 1128.93, what was + * actually measured was something more like 1128.9287653857625442 for example. + * Given that the double epsilon is on the order of 0.0000000000000009, you would + * need to provide sixteen significant figures of expected value for this kind of + * test to pass even with a typical test for floating point "approximate equality." + * That is clearly not required or desired. You really want to be able to provide + * 1128.93 along with a tolerance just like you provided 12 inches +- 0.005 inch + * above. + * + * This assertion is designed for real measurements by taking into account + * measurement tolerances. By doing so it also automatically compensates for + * floating point rounding errors. If you really want to check floating point + * equality down to the numeric_limits::epsilon () range, consider using + * ns3::TestDoubleIsEqual(). + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_EXPECT_MSG_EQ_TOL (snr, 1128.93, 0.005, "wrong snr (" << snr << ") in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param tol Tolerance of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_EXPECT_MSG_EQ_TOL(actual, limit, tol, msg) \ + NS_TEST_EXPECT_MSG_EQ_TOL_INTERNAL (actual, limit, tol, msg, __FILE__, __LINE__) + +// =========================================================================== +// Test for inequality +// =========================================================================== + +/** + * \internal + */ +#define NS_TEST_ASSERT_MSG_NE_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) != (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) NE " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + return true; \ + } \ + } while (false) + +/** + * \brief Test that an actual and expected (limit) value are equal and report + * and abort if not. + * + * Check to see if the expected (limit) value is not equal to the actual value + * found in a test case. If the two values are equal nothing happens, but if + * the comparison fails, an error is reported in a consistent way and the + * execution of the current test case is aborted. + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_ASSERT_MSG_NE (result, false, + * "cannot open file " << filename << " in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param msg Message that is output if the test does not pass. + * + * \warning Do not use this macro if you are comparing floating point numbers + * (float or double). Use NS_TEST_ASSERT_MSG_FLNE instead. + */ +#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg) \ + NS_TEST_ASSERT_MSG_NE_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +/** + * \internal + * + * Required to avoid use of return statement which allows use in methods + * (callbacks) returning void. + */ +#define NS_TEST_EXPECT_MSG_NE_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) != (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) NE " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + } \ + } while (false) + +/** + * \brief Test that an actual and expected (limit) value are equal and report + * if not. + * + * Check to see if the expected (limit) value is equal to the actual value + * found in a test case. If the two values are equal nothing happens, but if + * the comparison fails, an error is reported in a consistent way. EXPECT* + * macros do not return if an error is detected. + * + * The message is interpreted as a stream, for example: + * + * \code + * NS_TEST_EXPECT_MSG_EQUAL (result, true, + * "cannot open file " << filename << " in test"); + * \endcode + * + * is legal. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the expected value of the test. + * \param msg Message that is output if the test does not pass. + * + * \warning Do not use this macro if you are comparing floating point numbers + * (float or double). Use NS_TEST_EXPECT_MSG_FLNE instead. + */ +#define NS_TEST_EXPECT_MSG_NE(actual, limit, msg) \ + NS_TEST_EXPECT_MSG_NE_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +// =========================================================================== +// Test for less than relation +// =========================================================================== + +/** + * \internal + */ +#define NS_TEST_ASSERT_MSG_LT_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) < (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) LT " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + return true; \ + } \ + } while (false) + +/** + * \brief Test that an actual value is less than a limit and report and abort + * if not. + * + * Check to see if the actual value found in a test case is less than the + * limit value. If the actual value is lesser nothing happens, but if the + * check fails, an error is reported in a consistent way and the execution + * of the current test case is aborted. + * + * The message is interpreted as a stream. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the limit value of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_ASSERT_MSG_LT(actual, limit, msg) \ + NS_TEST_ASSERT_MSG_LT_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +/** + * \internal + * + * Required to avoid use of return statement which allows use in methods + * (callbacks) returning void. + */ +#define NS_TEST_EXPECT_MSG_LT_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) < (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) LT " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + } \ + } while (false) + +/** + * \brief Test that an actual value is less than a limit and report if not. + * + * Check to see if the actual value found in a test case is less than the + * limit value. If the actual value is lesser nothing happens, but if the + * check fails, an error is reported in a consistent way. EXPECT* macros do + * not return if an error is detected. + * + * The message is interpreted as a stream. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the limit value of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_EXPECT_MSG_LT(actual, limit, msg) \ + NS_TEST_EXPECT_MSG_LT_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +// =========================================================================== +// Test for greater than relation +// =========================================================================== + +/** + * \internal + */ +#define NS_TEST_ASSERT_MSG_GT_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) > (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) GT " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + return true; \ + } \ + } while (false) + +/** + * \brief Test that an actual value is greater than a limit and report and abort + * if not. + * + * Check to see if the actaul value found in a test case is greater than the + * limit value. If the actual value is greater nothing happens, but if the + * check fails, an error is reported in a consistent way and the execution + * of the current test case is aborted. + * + * The message is interpreted as a stream. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the limit value of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_ASSERT_MSG_GT(actual, limit, msg) \ + NS_TEST_ASSERT_MSG_GT_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +/** + * \internal + * + * Required to avoid use of return statement which allows use in methods + * (callbacks) returning void. + */ +#define NS_TEST_EXPECT_MSG_GT_INTERNAL(actual, limit, msg, file, line) \ + do { \ + if (!((actual) > (limit))) \ + { \ + std::ostringstream msgStream; \ + msgStream << msg; \ + std::ostringstream actualStream; \ + actualStream << actual; \ + std::ostringstream limitStream; \ + limitStream << limit; \ + ReportFailure (std::string (#actual) + " (actual) GT " + std::string (#limit) + " (limit)", \ + actualStream.str (), limitStream.str (), msgStream.str (), file, line); \ + } \ + } while (false) + +/** + * \brief Test that an actual value is greater than a limit and report if not. + * + * Check to see if the actual value found in a test case is greater than the + * limit value. If the actual value is greater nothing happens, but if the + * check fails, an error is reported in a consistent way. EXPECT* macros do + * not return if an error is detected. + * + * The message is interpreted as a stream. + * + * \param actual Expression for the actual value found during the test. + * \param limit Expression for the limit value of the test. + * \param msg Message that is output if the test does not pass. + */ +#define NS_TEST_EXPECT_MSG_GT(actual, limit, msg) \ + NS_TEST_EXPECT_MSG_GT_INTERNAL (actual, limit, msg, __FILE__, __LINE__) + +namespace ns3 { + +/** + * \brief Compare two double precision floating point numbers and declare them + * equal if they are within some epsilon of each other. + * + * Approximate comparison of floating point numbers near equality is trickier + * than one may expect and is well-discussed in the literature. Basic + * strategies revolve around a suggestion by Knuth to compare the floating + * point numbers as binary integers, supplying a maximum difference between + * them . This max difference is specified in Units in the Last Place (ulps) + * or a floating point epsilon. + * + * This routine is based on the GNU Scientific Library function gsl_fcmp. + * + * \param a The first of double precision floating point numbers to compare + * \param a The second of double precision floating point numbers to compare + * \param epsilon The second of double precision floating point numberss to compare + * \returns Returns true if the doubles are equal to a precision defined by epsilon + */ + bool TestDoubleIsEqual (const double a, const double b, const double epsilon = std::numeric_limits::epsilon ()); + +/** + * \brief A single test case. + */ +class TestCase +{ +public: + TestCase (std::string name); + virtual ~TestCase (); + + /** + * \brief Run this test case. + * \param verbose Turn on any output the test case may provide + * \returns Boolean sense of "an error has occurred." + */ + bool Run (void); + + /** + * \brief Set the verbosity of this test case. + * \param verbose Whether or not to print "stuff." + */ + void SetVerbose (bool verbose); + + /** + * \brief Set the name of this test case. + */ + void SetName (std::string name); + + /** + * \brief Get the name of this test case. + */ + std::string GetName (void); + + /** + * \brief Set the base directory of the ns-3 distribution. + */ + void SetBaseDir (std::string dir); + + /** + * \brief Get the base directory of the ns-3 distribution. + */ + std::string GetBaseDir (void); + +/** + * \brief Get the source directory of the current source file. + * + * One of the basic models of the test environment is that dedicated test- + * and response vectors live in the same directory as the source file. So it + * is a common operation to figure out what directory a given source file lives + * in. + * + * __FILE__ provides almost all of what we need, but in the gnu toolchain it + * comes out as something like "../src/core/pcap-file-test-suite.cc". + * + * We really don't want to have any dependency on the directory out of which a + * test is run, so we ask that any test-runner give us the base directory of the + * distribution, which is set via TestCase::SetBaseDir(). That string will look + * something like "/home/user/repos/ns-3-allinone/ns-3-dev". + * + * This function stitches the two pieces together and removes the file name to + * return something like "/home/user/repos/ns-3-allinone/ns-3-dev/src/core/". + * + * \param file The current source file name relative to the base directory. + * \returns The current source directory. + * + * \warning Always call this function as GetSourceDir (__FILE__) or use the + * convenience macro NS_TEST_SOURCEDIR. + */ + std::string GetSourceDir (std::string file); + + /** + * \brief Set the stream to which status and result messages will be written. + * + * We really don't want to have to pass an ofstream around to every function + * and we especially don't want to have to make our clients plumb an ofstream + * around so we need to save it. Since file streams are not designed to be + * copied or assigned (what does it mean to have duplicate streams to a file) + * we have to stash a pointer to the stream. + */ + void SetStream (std::ofstream *ofs); + + /** + * \brief Get the stream to which status and result messages will be written. + */ + std::ofstream *GetStream (void); + + /** + * \brief Manually Set the error status of this test case. + */ + void SetErrorStatus (bool error); + + /** + * \brief Get the error status of this test case. + */ + bool GetErrorStatus (void); + + void ReportStart (void); + void ReportSuccess (void); + void ReportFailure (std::string cond, std::string actual, std::string limit, std::string message, + std::string file, int32_t line); + void ReportEnd (void); + +protected: + /** + * \internal + * \brief Implementation of reporting method for the start of the test case. + */ + virtual void DoReportStart (void); + + /** + * \internal + * \brief Implementation of reporting method for success of the test case. + */ + virtual void DoReportSuccess (void); + + /** + * \internal + * \brief Implementation of reporting method for failure of the test case. + */ + virtual void DoReportFailure (std::string cond, std::string actual, std::string limit, std::string message, + std::string file, int32_t line); + + /** + * \internal + * \brief Implementation of reporting method for the end of the test case. + */ + virtual void DoReportEnd (void); + + /** + * \internal + * \param verbose Turn on any output the test case may provide + * \brief Implementation to do any local setup required for this test case. + */ + virtual void DoSetup (void); + + /** + * \internal + * \brief Implementation to actually run this test case. + * \param verbose Turn on any output the test case may provide + * \returns Boolean sense of "an error has occurred." + */ + virtual bool DoRun (void) = 0; + + /** + * \internal + * \param verbose Turn on any output the test case may provide + * \brief Implementation to do any local setup required for this test case. + */ + virtual void DoTeardown (void); + +private: + TestCase (TestCase& tc); + TestCase& operator= (TestCase& tc); + + std::string m_name; + bool m_verbose; + std::string m_basedir; + std::ofstream *m_ofs; + bool m_error; + clock_t m_startTime; + struct tms m_startTimes; +}; + +/** + * \brief A suite of tests to run. + */ +class TestSuite +{ +public: + enum TestType { + BVT = 1, /**< This test suite implements a Build Verification Test */ + UNIT, /**< This test suite implements a Unit Test */ + SYSTEM, /**< This test suite implements a System Test */ + EXAMPLE, /**< This test suite implements an Example Test */ + PERFORMANCE /**< This test suite implements a Performance Test */ + }; + + /** + * \brief Constuct a new test suite. + * + * \param name The name of the test suite. + * \param type The TestType of the test suite (defaults to UNIT test). + */ + TestSuite (std::string name, TestType type = UNIT); + + /** + * \brief Destroy a test suite. + */ + virtual ~TestSuite (); + + /** + * \brief Run this test suite. + * + * \param verbose Turn on any output the test case may provide + * \returns Boolean sense of "an error has occurred." + */ + bool Run (void); + + /** + * \brief Add an individual test case to this test suite. + * + * \param testCase Pointer to the test case object to be added. + * \returns Integer assigned as identifer of the provided test case. + */ + uint32_t AddTestCase (TestCase *testCase); + + /** + * \brief Get the number of test cases that have been added to this test suite. + * + * \returns Number of test cases in the suite. + */ + uint32_t GetNTestCases (void); + + /** + * \brief Get the test case at index i. + */ + TestCase *GetTestCase (uint32_t i); + + /** + * \brief get the kind of test this test suite implements + * + * \returns the TestType of the suite. + */ + TestType GetTestType (void); + + /** + * \brief Set the verbosity of this test suite. + * \param verbose Whether or not to print "stuff." + */ + void SetVerbose (bool verbose); + + /** + * \brief Set the name of this test suite. + */ + void SetName (std::string name); + + /** + * \brief Get the name of this test suite. + */ + std::string GetName (void); + + /** + * \brief Set the base directory of the ns-3 distribution. + */ + void SetBaseDir (std::string basedir); + + /** + * \brief Get the base directory of the ns-3 distribution. + */ + std::string GetBaseDir (void); + + /** + * \brief Set the stream to which status and result messages will be written. + * + * We really don't want to have to pass an ofstream around to every function + * and we especially don't want to have to make our clients plumb an ofstream + * around so we need to save it. Since file streams are not designed to be + * copied or assigned (what does it mean to have duplicate streams to a file) + * we have to stash a pointer to the stream. + */ + void SetStream (std::ofstream *ofs); + + void ReportStart (void); + void ReportSuccess (void); + void ReportFailure (void); + void ReportEnd (void); + +protected: + /** + * \internal + * \brief Implementation of reporting method for the start of the test suite. + */ + virtual void DoReportStart (void); + + /** + * \internal + * \brief Implementation of reporting method for success of the test suite. + */ + virtual void DoReportSuccess (void); + + /** + * \internal + * \brief Implementation of reporting method for failure of the test suite. + */ + virtual void DoReportFailure (void); + + /** + * \internal + * \brief Implementation of reporting method for the end of the test suite. + */ + virtual void DoReportEnd (void); + + /** + * \internal + * \param verbose Turn on any output the test case may provide + * \brief Implementation to do any local setup required for this test suite. + */ + virtual void DoSetup (void); + + /** + * \internal + * \brief Implementation to actually run this test suite. + * \param verbose Turn on any output the test case may provide + * \returns Boolean sense of "an error has occurred." + */ + virtual bool DoRun (void); + + /** + * \internal + * \param verbose Turn on any output the test case may provide + * \brief Implementation to do any local setup required for this test suite. + */ + virtual void DoTeardown (void); + +private: + TestSuite (TestSuite& ts); + TestSuite& operator= (TestSuite& ts); + + std::string m_name; + bool m_verbose; + std::string m_basedir; + std::ofstream *m_ofs; + TestType m_type; + + clock_t m_startTime; + struct tms m_startTimes; + + typedef std::vector TestCaseVector_t; + TestCaseVector_t m_tests; +}; + +/** + * \brief A runner to execute tests. + */ +class TestRunner +{ +public: + static uint32_t AddTestSuite (TestSuite *testSuite); + static uint32_t GetNTestSuites (void); + static TestSuite *GetTestSuite (uint32_t n); +}; + +/** + * \brief A simple way to store test vectors (for stimulus or from responses) + */ +template +class TestVectors +{ +public: + TestVectors (); + virtual ~TestVectors (); + + void Reserve (uint32_t reserve); + + uint32_t Add (T vector); + + uint32_t GetN (void) const; + T Get (uint32_t i) const; + +private: + TestVectors (const TestVectors& tv); + TestVectors& operator= (const TestVectors& tv); + bool operator== (const TestVectors& tv) const; + + typedef std::vector TestVector_t; + TestVector_t m_vectors; +}; + +template +TestVectors::TestVectors () + : m_vectors () +{ +} + +template +void +TestVectors::Reserve (uint32_t reserve) +{ + m_vectors.reserve (reserve); +} + +template +TestVectors::~TestVectors () +{ +} + +template +uint32_t +TestVectors::Add (T vector) +{ + uint32_t index = m_vectors.size (); + m_vectors.push_back (vector); + return index; +} + +template +uint32_t +TestVectors::GetN (void) const +{ + return m_vectors.size (); +} + +template +T +TestVectors::Get (uint32_t i) const +{ + NS_ABORT_MSG_UNLESS (m_vectors.size () > i, "TestVectors::Get(): Bad index"); + return m_vectors[i]; +} + +}; // namespace ns3 + +// +// Original ns-3 unit test code for compatibility +// #ifdef RUN_SELF_TESTS namespace ns3 { diff --git a/src/core/wscript b/src/core/wscript index b75c99e8b..566e8b4f6 100644 --- a/src/core/wscript +++ b/src/core/wscript @@ -79,6 +79,7 @@ def build(bld): 'callback.cc', 'names.cc', 'vector.cc', + 'names-test-suite.cc', ] headers = bld.new_task_gen('ns3header') @@ -149,3 +150,5 @@ def build(bld): 'system-condition.h', ]) + if bld.env['ENABLE_GSL']: + core.source.extend(['rng-test-suite.cc']) diff --git a/src/devices/mesh/dot11s/airtime-metric.cc b/src/devices/mesh/dot11s/airtime-metric.cc new file mode 100644 index 000000000..cf6c27574 --- /dev/null +++ b/src/devices/mesh/dot11s/airtime-metric.cc @@ -0,0 +1,95 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + */ + +#include "airtime-metric.h" +#include "ns3/wifi-remote-station-manager.h" +#include "ns3/wifi-mode.h" +namespace ns3 { +namespace dot11s { +NS_OBJECT_ENSURE_REGISTERED (AirtimeLinkMetricCalculator); +TypeId +AirtimeLinkMetricCalculator::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::AirtimeLinkMetricCalculator") + .SetParent () + .AddConstructor () + .AddAttribute ( "OverheadNanosec", + "Overhead expressed in nanoseconds:DIFS+ 2* SIFS + 2* PREAMBLE + 2* ACK", + UintegerValue (108000), + MakeUintegerAccessor (&AirtimeLinkMetricCalculator::m_overheadNanosec), + MakeUintegerChecker (1) + ) + .AddAttribute ( "TestLength", + "Rate should be estimated using test length.", + UintegerValue (1024), + MakeUintegerAccessor ( + &AirtimeLinkMetricCalculator::m_testLength), + MakeUintegerChecker (1) + ) + .AddAttribute ( "Dot11MacHeaderLength", + "Length of the 802.11 header", + UintegerValue (36), + MakeUintegerAccessor ( + &AirtimeLinkMetricCalculator::m_headerLength), + MakeUintegerChecker (0) + ) + .AddAttribute ( "Dot11sMeshHeaderLength", + "Length of the mesh header", + UintegerValue (6), + MakeUintegerAccessor ( + &AirtimeLinkMetricCalculator::m_meshHeaderLength), + MakeUintegerChecker (0) + ) + ; + return tid; +} + +uint32_t +AirtimeLinkMetricCalculator::CalculateMetric (Mac48Address peerAddress, Ptr mac) +{ + /* Airtime link metric is defined in 11B.10 of 802.11s Draft D3.0 as: + * + * airtime = (O + Bt/r)* (1 + average retry counter), where + * o -- the PHY dependent channel access which includes frame headers, training sequences, + * access protocol frames, etc. + * bt -- the test packet length in bits (8192 by default), + * r -- the current bitrate of the packet, + * + * Final result is expressed in units of 0.01 Time Unit = 10.24 us (as required by 802.11s draft) + */ + + const double sec2ns = 1e9; // seconds -> nanoseconds conversion factor + const double ns2tu = 10240; // nanoseconds -> 0.01 TU conversion factor + + WifiRemoteStation * station = mac->GetStationManager ()->Lookup (peerAddress); + NS_ASSERT (station != 0); + Ptr test_frame = Create (m_testLength + m_headerLength + m_meshHeaderLength); + uint32_t rate = + station->GetDataMode (test_frame, m_testLength + m_headerLength + m_meshHeaderLength).GetDataRate (); + uint32_t payload_nanosec = (uint32_t) ( + (double) ((m_testLength + m_meshHeaderLength) * 8 /*octets -> bits*/) * sec2ns / ((double) rate)); + uint32_t header_nanosec = (uint32_t) ((double) (m_headerLength * 8 /*octets -> bits*/* sec2ns) + / ((double) mac->GetStationManager () -> GetBasicMode (0).GetDataRate ())); + uint32_t metric = (uint32_t) (((double) (payload_nanosec + header_nanosec + m_overheadNanosec)) / ns2tu + * (station->GetAvgSlrc () + 1)); + return metric; +} +} //namespace dot11s +} //namespace ns3 diff --git a/src/devices/mesh/dot11s/airtime-metric.h b/src/devices/mesh/dot11s/airtime-metric.h new file mode 100644 index 000000000..6755ac6b5 --- /dev/null +++ b/src/devices/mesh/dot11s/airtime-metric.h @@ -0,0 +1,59 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + */ + +#ifndef AIRTIME_METRIC_H +#define AIRTIME_METRIC_H +#include "ns3/mesh-wifi-interface-mac.h" +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * + * \brief airtime link metric calculator + * + * \details Airtime link metric is defined in 11B.10 of 802.11s Draft D3.0 as: + * + * airtime = (O + Bt/r)* (1 + average retry counter), where + * + * o -- the PHY dependent channel access which includes frame headers, training sequences, + * access protocol frames, etc. + * bt -- the test packet length in bits (8192 by default), + * r -- the current bitrate of the packet, + * + * Final result is expressed in units of 0.01 Time Unit = 10.24 us (as required by 802.11s draft) + */ +class AirtimeLinkMetricCalculator : public Object +{ +public: + static TypeId GetTypeId (); + uint32_t CalculateMetric (Mac48Address peerAddress, Ptr mac); +private: + //\brief Overhead expressed in nanoseconds:DIFS+ 2* SIFS + 2*PREAMBLE + 2* ACK + uint32_t m_overheadNanosec; + ///\brief Bt value + uint32_t m_testLength; + ///\brief header length (used in overhead) + uint16_t m_headerLength; + ///\brief meshHeader length (6 octets usually) + uint16_t m_meshHeaderLength; +}; +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/dot11s-mac-header.cc b/src/devices/mesh/dot11s/dot11s-mac-header.cc new file mode 100644 index 000000000..482dd95e1 --- /dev/null +++ b/src/devices/mesh/dot11s/dot11s-mac-header.cc @@ -0,0 +1,377 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#include "ns3/assert.h" +#include "ns3/address-utils.h" +#include "dot11s-mac-header.h" +#include "ns3/packet.h" +#include "ns3/test.h" + +namespace ns3 { +namespace dot11s { +/*********************************************************** + * Here Mesh Mac Header functionality is defined. + ***********************************************************/ +TypeId +MeshHeader::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Dot11sMacHeader") + .SetParent
    () + .AddConstructor (); + return tid; +} +MeshHeader::MeshHeader () : + m_meshFlags (0), m_meshTtl (0), m_meshSeqno (0), m_addr4 (Mac48Address ()), m_addr5 (Mac48Address ()), + m_addr6 (Mac48Address ()) +{ +} +MeshHeader::~MeshHeader () +{ +} +TypeId +MeshHeader::GetInstanceTypeId () const +{ + return GetTypeId (); +} +void +MeshHeader::SetAddr4 (Mac48Address address) +{ + m_addr4 = address; +} +void +MeshHeader::SetAddr5 (Mac48Address address) +{ + m_addr5 = address; +} +void +MeshHeader::SetAddr6 (Mac48Address address) +{ + m_addr6 = address; +} +Mac48Address +MeshHeader::GetAddr4 () const +{ + return m_addr4; +} +Mac48Address +MeshHeader::GetAddr5 () const +{ + return m_addr5; +} +Mac48Address +MeshHeader::GetAddr6 () const +{ + return m_addr6; +} +void +MeshHeader::SetMeshSeqno (uint32_t seqno) +{ + m_meshSeqno = seqno; +} +uint32_t +MeshHeader::GetMeshSeqno () const +{ + return m_meshSeqno; +} +void +MeshHeader::SetMeshTtl (uint8_t TTL) +{ + m_meshTtl = TTL; +} +uint8_t +MeshHeader::GetMeshTtl () const +{ + return m_meshTtl; +} +void +MeshHeader::SetAddressExt (uint8_t num_of_addresses) +{ + NS_ASSERT (num_of_addresses <= 3); + m_meshFlags |= 0x03 & num_of_addresses; +} +uint8_t +MeshHeader::GetAddressExt () const +{ + return (0x03 & m_meshFlags); +} +uint32_t +MeshHeader::GetSerializedSize () const +{ + return 6 + GetAddressExt () * 6; +} +void +MeshHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteU8 (m_meshFlags); + i.WriteU8 (m_meshTtl); + i.WriteHtolsbU32 (m_meshSeqno); + uint8_t addresses_to_add = GetAddressExt (); + //Writing Address extensions: + if ((addresses_to_add == 1) || (addresses_to_add == 3)) + { + WriteTo (i, m_addr4); + } + if (addresses_to_add > 1) + { + WriteTo (i, m_addr5); + } + if (addresses_to_add > 1) + { + WriteTo (i, m_addr6); + } +} +uint32_t +MeshHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + uint8_t addresses_to_read = 0; + m_meshFlags = i.ReadU8 (); + m_meshTtl = i.ReadU8 (); + m_meshSeqno = i.ReadLsbtohU32 (); + addresses_to_read = m_meshFlags & 0x03; + if ((addresses_to_read == 1) || (addresses_to_read == 3)) + { + ReadFrom (i, m_addr4); + } + if (addresses_to_read > 1) + { + ReadFrom (i, m_addr5); + } + if (addresses_to_read > 1) + { + ReadFrom (i, m_addr6); + } + return i.GetDistanceFrom (start); +} +void +MeshHeader::Print (std::ostream &os) const +{ + os << "flags = " << (uint16_t) m_meshFlags << std::endl << "ttl = " << (uint16_t) m_meshTtl + << std::endl << "seqno = " << m_meshSeqno << std::endl<< "addr4 = " << m_addr4 << std::endl + << "addr5 = " << m_addr5 << std::endl << "addr6 = " << m_addr6 << std::endl; +} +bool +operator== (const MeshHeader & a, const MeshHeader & b) +{ + return ((a.m_meshFlags == b.m_meshFlags) && (a.m_meshTtl == b.m_meshTtl) + && (a.m_meshSeqno == b.m_meshSeqno) && (a.m_addr4 == b.m_addr4) && (a.m_addr5 == b.m_addr5) + && (a.m_addr6 == b.m_addr6)); +} +/********************************************************** + * ActionFrame + **********************************************************/ +WifiMeshActionHeader::WifiMeshActionHeader () +{ +} +WifiMeshActionHeader::~WifiMeshActionHeader () +{ +} +void +WifiMeshActionHeader::SetAction (WifiMeshActionHeader::CategoryValue type, + WifiMeshActionHeader::ActionValue action) +{ + m_category = type; + + switch (type) + { + case MESH_PEERING_MGT: + { + m_actionValue = action.peerLink; + break; + } + case MESH_PATH_SELECTION: + { + m_actionValue = action.pathSelection; + break; + } + case MESH_LINK_METRIC: + case MESH_INTERWORKING: + case MESH_RESOURCE_COORDINATION: + case MESH_PROXY_FORWARDING: + break; + } +} +WifiMeshActionHeader::CategoryValue +WifiMeshActionHeader::GetCategory () +{ + switch (m_category) + { + case MESH_PEERING_MGT: + return MESH_PEERING_MGT; + case MESH_LINK_METRIC: + return MESH_LINK_METRIC; + case MESH_PATH_SELECTION: + return MESH_PATH_SELECTION; + case MESH_INTERWORKING: + return MESH_INTERWORKING; + case MESH_RESOURCE_COORDINATION: + return MESH_RESOURCE_COORDINATION; + case MESH_PROXY_FORWARDING: + return MESH_PROXY_FORWARDING; + default: + NS_FATAL_ERROR ("Unknown action value"); + return MESH_PEERING_MGT; + } +} +WifiMeshActionHeader::ActionValue +WifiMeshActionHeader::GetAction () +{ + ActionValue retval; + retval.peerLink = PEER_LINK_OPEN; // Needs to be initialized to something to quiet valgrind in default cases + switch (m_category) + { + case MESH_PEERING_MGT: + switch (m_actionValue) + { + case PEER_LINK_OPEN: + retval.peerLink = PEER_LINK_OPEN; + return retval; + case PEER_LINK_CONFIRM: + retval.peerLink = PEER_LINK_CONFIRM; + return retval; + case PEER_LINK_CLOSE: + retval.peerLink = PEER_LINK_CLOSE; + return retval; + default: + NS_FATAL_ERROR ("Unknown mesh peering management action code"); + return retval; + } + case MESH_PATH_SELECTION: + switch (m_actionValue) + { + case PATH_SELECTION: + retval.pathSelection = PATH_SELECTION; + return retval; + default: + NS_FATAL_ERROR ("Unknown mesh path selection action code"); + return retval; + } + case MESH_LINK_METRIC: + // not yet supported + case MESH_INTERWORKING: + // not yet supported + case MESH_RESOURCE_COORDINATION: + // not yet supported + default: + NS_FATAL_ERROR ("Unsupported mesh action"); + return retval; + } +} +TypeId +WifiMeshActionHeader::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::WifiMeshActionHeader") + .SetParent
    () + .AddConstructor (); + return tid; +} +TypeId +WifiMeshActionHeader::GetInstanceTypeId () const +{ + return GetTypeId (); +} +void +WifiMeshActionHeader::Print (std::ostream &os) const +{ +} +uint32_t +WifiMeshActionHeader::GetSerializedSize () const +{ + return 2; +} +void +WifiMeshActionHeader::Serialize (Buffer::Iterator start) const +{ + start.WriteU8 (m_category); + start.WriteU8 (m_actionValue); +} +uint32_t +WifiMeshActionHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + m_category = i.ReadU8 (); + m_actionValue = i.ReadU8 (); + return i.GetDistanceFrom (start); +} +#ifdef RUN_SELF_TESTS + +/// Built-in self test for Dot11sMacHeader +struct Dot11sMacHeaderBist : public Test +{ + Dot11sMacHeaderBist () : + Test ("Mesh/802.11s/MeshHeader") + { + } + virtual bool + RunTests (); +}; + +/// Test instance +static Dot11sMacHeaderBist g_Dot11sMacHeaderBist; + +bool +Dot11sMacHeaderBist::RunTests () +{ + bool result (true); + { + MeshHeader a; + a.SetAddressExt (3); + a.SetAddr4 (Mac48Address ("11:22:33:44:55:66")); + a.SetAddr5 (Mac48Address ("11:00:33:00:55:00")); + a.SetAddr6 (Mac48Address ("00:22:00:44:00:66")); + a.SetMeshTtl (122); + a.SetMeshSeqno (321); + Ptr packet = Create (); + packet->AddHeader (a); + MeshHeader b; + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + { + MeshHeader a; + a.SetAddressExt (2); + a.SetAddr5 (Mac48Address ("11:00:33:00:55:00")); + a.SetAddr6 (Mac48Address ("00:22:00:44:00:66")); + a.SetMeshTtl (122); + a.SetMeshSeqno (321); + Ptr packet = Create (); + packet->AddHeader (a); + MeshHeader b; + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + { + MeshHeader a; + a.SetAddressExt (1); + a.SetAddr4 (Mac48Address ("11:22:33:44:55:66")); + a.SetMeshTtl (122); + a.SetMeshSeqno (321); + Ptr packet = Create (); + packet->AddHeader (a); + MeshHeader b; + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + return result; +} +#endif +} //namespace dot11s +} // namespace ns3 diff --git a/src/devices/mesh/dot11s/dot11s-mac-header.h b/src/devices/mesh/dot11s/dot11s-mac-header.h new file mode 100644 index 000000000..57803b53a --- /dev/null +++ b/src/devices/mesh/dot11s/dot11s-mac-header.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + + +#ifndef MESH_WIFI_MAC_HEADER_H +#define MESH_WIFI_MAC_HEADER_H + +#include "ns3/header.h" +#include "ns3/mac48-address.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * + * \brief Mesh Control field, see IEEE 802.11s draft 3.0 section 7.1.3.5b + * + * Header format: | Mesh flags: 1 | TTL: 1 | Sequence number: 4 | Address ext.: 0, 6, 12 or 18 | + */ +class MeshHeader : public Header +{ +public: + MeshHeader (); + ~MeshHeader (); + static TypeId GetTypeId (); + virtual TypeId GetInstanceTypeId () const; + virtual void Print (std::ostream &os) const; + + void SetAddr4 (Mac48Address address); + void SetAddr5 (Mac48Address address); + void SetAddr6 (Mac48Address address); + Mac48Address GetAddr4 () const; + Mac48Address GetAddr5 () const; + Mac48Address GetAddr6 () const; + + void SetMeshSeqno (uint32_t seqno); + uint32_t GetMeshSeqno () const; + + void SetMeshTtl (uint8_t TTL); + uint8_t GetMeshTtl () const; + + void SetAddressExt (uint8_t num_of_addresses); + uint8_t GetAddressExt () const; + + virtual uint32_t GetSerializedSize () const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); +private: + uint8_t m_meshFlags; + uint8_t m_meshTtl; + uint32_t m_meshSeqno; + Mac48Address m_addr4; + Mac48Address m_addr5; + Mac48Address m_addr6; + friend bool operator== (const MeshHeader & a, const MeshHeader & b); +}; +bool operator== (const MeshHeader & a, const MeshHeader & b); + +/** + * \ingroup dot11s + * + * \brief See IEEE 802.11s draft 3.0 section 7.2.3.14 + * + * Header format: | category: 1 | action value: 1 | + */ +class WifiMeshActionHeader : public Header +{ +public: + WifiMeshActionHeader (); + ~WifiMeshActionHeader (); + + /* Compatible with open80211s implementation */ + enum CategoryValue //table 7-24 staring from 4 + { + MESH_PEERING_MGT = 30, + MESH_LINK_METRIC = 31, + MESH_PATH_SELECTION = 32, + MESH_INTERWORKING = 33, + MESH_RESOURCE_COORDINATION = 34, + MESH_PROXY_FORWARDING = 35, + }; + /* Compatible with open80211s implementation */ + enum PeerLinkMgtActionValue + { + PEER_LINK_OPEN = 0, + PEER_LINK_CONFIRM = 1, + PEER_LINK_CLOSE = 2, + }; + enum LinkMetricActionValue + { + LINK_METRIC_REQUEST = 0, + LINK_METRIC_REPORT, + }; + /* Compatible with open80211s implementation */ + enum PathSelectionActionValue + { + PATH_SELECTION = 0, + }; + enum InterworkActionValue + { + PORTAL_ANNOUNCEMENT = 0, + }; + enum ResourceCoordinationActionValue + { + CONGESTION_CONTROL_NOTIFICATION = 0, + MDA_SETUP_REQUEST, + MDA_SETUP_REPLY, + MDAOP_ADVERTISMENT_REQUEST, + MDAOP_ADVERTISMENTS, + MDAOP_SET_TEARDOWN, + BEACON_TIMING_REQUEST, + BEACON_TIMING_RESPONSE, + TBTT_ADJUSTMENT_REQUEST, + MESH_CHANNEL_SWITCH_ANNOUNCEMENT, + }; + typedef union + { + enum PeerLinkMgtActionValue peerLink; + enum LinkMetricActionValue linkMetrtic; + enum PathSelectionActionValue pathSelection; + enum InterworkActionValue interwork; + enum ResourceCoordinationActionValue resourceCoordination; + } ActionValue; + void SetAction (enum CategoryValue type,ActionValue action); + + CategoryValue GetCategory (); + ActionValue GetAction (); + static TypeId GetTypeId (); + virtual TypeId GetInstanceTypeId () const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize () const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); +private: + uint8_t m_category; + uint8_t m_actionValue; +}; +} //namespace dot11s +} // namespace ns3 +#endif /* MESH_WIFI_MAC_HEADER_H */ diff --git a/src/devices/mesh/dot11s/dot11s.h b/src/devices/mesh/dot11s/dot11s.h new file mode 100644 index 000000000..d3087b666 --- /dev/null +++ b/src/devices/mesh/dot11s/dot11s.h @@ -0,0 +1,79 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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: Pavel Boyko + * + * This is doxygen module description, don't include + */ +/** + * \ingroup mesh + * \defgroup dot11s IEEE 802.11s draft + * + * \brief IEEE 802.11s (mesh) draft standard implementation + * + * Current model conforms IEEE 802.11s D3.0 draft version and includes + * Peer Management Protocol and HWMP (routing) Protocol implementations. + * + * The multi-interface mesh points are supported as an + * extension of IEEE draft version 3.0. Note that corresponding helper + * creates single interface station by default. + * \section Dot11s Overview of IEEE 802.11s + * Implementation of 802.11s draft standard consists of two main parts: + * Peer management protocol and HWMP - Hybrid Wireless Mesh Protocol. + * + * The task of peer management protocol is the following: + * -open links detecting beacons and starting peer link finite + * state machine. + * -close peer links due to transmission failures or beacon loss. + * + * If peer link between sender and receiver does not exist, the packet will be dropped. + * So, the plug-in to peer management protocol is the first in the list of + * ns3::MeshWifiInterfaceMacPlugin + * \subsection IE Implementation of Information elements + * To make a model of 802.11s stack, we need to implement large quantity of information + * elements and have an ability to add them in a beacon. Also we have to parse beacon + * and extract proper information elements from it. So, two classes ns3::WifiInformationElement + * and ns3::WifiInformationElementVector support this functionality. The former keeps all + * internal structure of information element and its elementId. It has methods + * ns3::WifiInformationElement::SerializeInformation and ns3::WifiInformationElement::DeserializeInformation. + * The latter is ns3::WifiInformationElementVector adds all stored information elements to a packet. + * \subsection PMP Peer management protocol + * Peer management protocol consists of three main parts: + * - Protocol itself ns3::dot11s::PeerManagementProtocol, which keeps all active peer links on interfaces, + * handles all changes of their states and notifies a routing protocol about link failures. + * - MAC plug-in ns3::dot11s::PeerManagementProtocolMac which drops packet, if there is no peer link, + * and peek all needed information from management frames and information elements from beacons. + * - Peer link ns3::dot11s::PeerLink which keeps finite state machine of each peer link, keeps + * beacon loss counter and counter of successive transmission failures. + * + * Procedure of closing peer link is not described detailed in 802.11s draft standard, so in our model + * the link may be closed by: + * - beacon loss (see an appropriate attribute of ns3::dot11s::PeerLink class) + * - transmission failure -- when a predefined number of successive packets have failed to transmit, + * the link will be closed. + * + * Also Peer management protocol is responsible for beacon collision avoidance, because it keeps + * beacon timing elements from all neighbours. + * Note, that PeerManagementProtocol is not attached to MeshPointDevice as a routing protocol, + * but the structure is similar: the upper tier of protocol ns3::dot11s::PeerManagementProtocol + * and its plug-in is ns3::dot11s::PeerManagementProtocolMac. + * + * \subsection HWMP Hybrid Wireless Mesh Protocol + * HWMP is implemented in both modes -- reactive and proactive. Also we have implemented an ability + * to transmit broadcast data and management frames as unicasts (see appropriate attributes). + * This feature turns off at a station when the number of neighbours of the station is more than a threshold. + */ diff --git a/src/devices/mesh/dot11s/hwmp-protocol-mac.cc b/src/devices/mesh/dot11s/hwmp-protocol-mac.cc new file mode 100644 index 000000000..3434c84c9 --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-protocol-mac.cc @@ -0,0 +1,475 @@ +/* -*- 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 + */ + +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/packet.h" +#include "ns3/simulator.h" +#include "ns3/nstime.h" +#include "ns3/log.h" +#include "dot11s-mac-header.h" +#include "hwmp-protocol-mac.h" +#include "hwmp-tag.h" +#include "ie-dot11s-preq.h" +#include "ie-dot11s-prep.h" +#include "ie-dot11s-rann.h" +#include "ie-dot11s-perr.h" + +namespace ns3 { +namespace dot11s { + +NS_LOG_COMPONENT_DEFINE ("HwmpProtocolMac"); +HwmpProtocolMac::HwmpProtocolMac (uint32_t ifIndex, Ptr protocol) : + m_ifIndex (ifIndex), m_protocol (protocol) +{ +} +HwmpProtocolMac::~HwmpProtocolMac () +{ +} +void +HwmpProtocolMac::SetParent (Ptr parent) +{ + m_parent = parent; +} + +bool +HwmpProtocolMac::ReceiveData (Ptr packet, const WifiMacHeader & header) +{ + NS_ASSERT (header.IsData ()); + + MeshHeader meshHdr; + HwmpTag tag; + if (packet->PeekPacketTag (tag)) + { + NS_FATAL_ERROR ("HWMP tag is not supposed to be received by network"); + } + + packet->RemoveHeader (meshHdr); + m_stats.rxData++; + m_stats.rxDataBytes += packet->GetSize (); + + //TODO: address extension + Mac48Address destination; + Mac48Address source; + switch (meshHdr.GetAddressExt ()) + { + case 0: + source = header.GetAddr4 (); + destination = header.GetAddr3 (); + break; + default: + NS_FATAL_ERROR ( + "6-address scheme is not yet supported and 4-address extension is not supposed to be used for data frames."); + } + tag.SetSeqno (meshHdr.GetMeshSeqno ()); + tag.SetTtl (meshHdr.GetMeshTtl ()); + packet->AddPacketTag (tag); + + if ((destination == Mac48Address::GetBroadcast ()) && (m_protocol->DropDataFrame (meshHdr.GetMeshSeqno (), + source))) + { + return false; + } + return true; +} + +bool +HwmpProtocolMac::ReceiveAction (Ptr packet, const WifiMacHeader & header) +{ + m_stats.rxMgt++; + m_stats.rxMgtBytes += packet->GetSize (); + WifiMeshActionHeader actionHdr; + packet->RemoveHeader (actionHdr); + WifiMeshActionHeader::ActionValue actionValue = actionHdr.GetAction (); + if (actionHdr.GetCategory () != WifiMeshActionHeader::MESH_PATH_SELECTION) + { + return true; + } + WifiInformationElementVector elements; + packet->RemoveHeader (elements); + std::vector failedDestinations; + for (WifiInformationElementVector::Iterator i = elements.Begin(); i != elements.End(); i ++) + { + if ((*i)->ElementId () == IE11S_RANN) + { + NS_LOG_WARN ("RANN is not supported!"); + } + if ((*i)->ElementId () == IE11S_PREQ) + { + Ptr preq = DynamicCast (*i); + NS_ASSERT (preq != 0); + m_stats.rxPreq++; + if (preq->GetOriginatorAddress () == m_protocol->GetAddress ()) + { + continue; + } + if (preq->GetTtl () == 0) + { + continue; + } + preq->DecrementTtl (); + m_protocol->ReceivePreq (*preq, header.GetAddr2 (), m_ifIndex, header.GetAddr3 (), + m_parent->GetLinkMetric (header.GetAddr2 ())); + } + if ((*i)->ElementId () == IE11S_PREP) + { + Ptr prep = DynamicCast (*i); + NS_ASSERT (prep != 0); + m_stats.rxPrep++; + if (prep->GetTtl () == 0) + { + continue; + } + prep->DecrementTtl (); + m_protocol->ReceivePrep (*prep, header.GetAddr2 (), m_ifIndex, header.GetAddr3 (), + m_parent->GetLinkMetric (header.GetAddr2 ())); + } + if ((*i)->ElementId () == IE11S_PERR) + { + Ptr perr = DynamicCast (*i); + NS_ASSERT (perr != 0); + m_stats.rxPerr++; + std::vector destinations = perr->GetAddressUnitVector (); + for (std::vector::const_iterator i = destinations.begin (); i + != destinations.end (); i++) + { + failedDestinations.push_back (*i); + } + } + } + if (failedDestinations.size () > 0) + { + m_protocol->ReceivePerr (failedDestinations, header.GetAddr2 (), m_ifIndex, header.GetAddr3 ()); + } + NS_ASSERT (packet->GetSize () == 0); + return false; +} + +bool +HwmpProtocolMac::Receive (Ptr packet, const WifiMacHeader & header) +{ + if (header.IsData ()) + { + return ReceiveData (packet, header); + } + else + { + if (header.IsAction ()) + { + return ReceiveAction (packet, header); + } + else + { + return true; // don't care + } + } +} +bool +HwmpProtocolMac::UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, + Mac48Address to) +{ + if (!header.IsData ()) + { + return true; + } + HwmpTag tag; + bool tagExists = packet->RemovePacketTag (tag); + if (!tagExists) + { + NS_FATAL_ERROR ("HWMP tag must exist at this point"); + } + m_stats.txData++; + m_stats.txDataBytes += packet->GetSize (); + MeshHeader meshHdr; + meshHdr.SetMeshSeqno (tag.GetSeqno ()); + meshHdr.SetMeshTtl (tag.GetTtl ()); + packet->AddHeader (meshHdr); + header.SetAddr1 (tag.GetAddress ()); + return true; +} +WifiMeshActionHeader +HwmpProtocolMac::GetWifiMeshActionHeader () +{ + WifiMeshActionHeader actionHdr; + WifiMeshActionHeader::ActionValue action; + action.pathSelection = WifiMeshActionHeader::PATH_SELECTION; + actionHdr.SetAction (WifiMeshActionHeader::MESH_PATH_SELECTION, action); + return actionHdr; +} +void +HwmpProtocolMac::SendPreq (IePreq preq) +{ + NS_LOG_FUNCTION_NOARGS (); + std::vector preq_vector; + preq_vector.push_back (preq); + SendPreq (preq_vector); +} +void +HwmpProtocolMac::SendPreq (std::vector preq) +{ + Ptr packet = Create (); + WifiInformationElementVector elements; + for (std::vector::iterator i = preq.begin (); i != preq.end (); i++) + { + elements.AddInformationElement(Ptr (&(*i))); + } + packet->AddHeader(elements); + packet->AddHeader (GetWifiMeshActionHeader ()); + //create 802.11 header: + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + hdr.SetAddr2 (m_parent->GetAddress ()); + hdr.SetAddr3 (m_protocol->GetAddress ()); + //Send Management frame + std::vector receivers = m_protocol->GetPreqReceivers (m_ifIndex); + for (std::vector::const_iterator i = receivers.begin (); i != receivers.end (); i++) + { + hdr.SetAddr1 (*i); + m_stats.txPreq++; + m_stats.txMgt++; + m_stats.txMgtBytes += packet->GetSize (); + m_parent->SendManagementFrame (packet, hdr); + } +} +void +HwmpProtocolMac::RequestDestination (Mac48Address dst, uint32_t originator_seqno, uint32_t dst_seqno) +{ + NS_LOG_FUNCTION_NOARGS (); + for (std::vector::iterator i = m_myPreq.begin (); i != m_myPreq.end (); i++) + { + if (i->IsFull ()) + { + continue; + } + NS_ASSERT (i->GetDestCount () > 0); + i->AddDestinationAddressElement (m_protocol->GetDoFlag (), m_protocol->GetRfFlag (), dst, dst_seqno); + } + IePreq preq; + preq.SetHopcount (0); + preq.SetTTL (m_protocol->GetMaxTtl ()); + preq.SetPreqID (m_protocol->GetNextPreqId ()); + preq.SetOriginatorAddress (m_protocol->GetAddress ()); + preq.SetOriginatorSeqNumber (originator_seqno); + preq.SetLifetime (m_protocol->GetActivePathLifetime ()); + preq.AddDestinationAddressElement (m_protocol->GetDoFlag (), m_protocol->GetRfFlag (), dst, dst_seqno); + m_myPreq.push_back (preq); + SendMyPreq (); +} +void +HwmpProtocolMac::SendMyPreq () +{ + NS_LOG_FUNCTION_NOARGS (); + if (m_preqTimer.IsRunning ()) + { + return; + } + if (m_myPreq.size () == 0) + { + return; + } + //reschedule sending PREQ + NS_ASSERT (!m_preqTimer.IsRunning ()); + m_preqTimer = Simulator::Schedule (m_protocol->GetPreqMinInterval (), &HwmpProtocolMac::SendMyPreq, this); + SendPreq (m_myPreq); + m_myPreq.clear (); +} +void +HwmpProtocolMac::SendPrep (IePrep prep, Mac48Address receiver) +{ + NS_LOG_FUNCTION_NOARGS (); + //Create packet + Ptr packet = Create (); + WifiInformationElementVector elements; + elements.AddInformationElement(Ptr (&prep)); + packet->AddHeader (elements); + packet->AddHeader (GetWifiMeshActionHeader ()); + //create 802.11 header: + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + hdr.SetAddr1 (receiver); + hdr.SetAddr2 (m_parent->GetAddress ()); + hdr.SetAddr3 (m_protocol->GetAddress ()); + //Send Management frame + m_stats.txPrep++; + m_stats.txMgt++; + m_stats.txMgtBytes += packet->GetSize (); + m_parent->SendManagementFrame (packet, hdr); +} +void +HwmpProtocolMac::ForwardPerr (std::vector failedDestinations, std::vector< + Mac48Address> receivers) +{ + NS_LOG_FUNCTION_NOARGS (); + Ptr packet = Create (); + Ptr perr = Create (); + WifiInformationElementVector elements; + for (std::vector::const_iterator i = failedDestinations.begin (); i + != failedDestinations.end (); i++) + { + if (!perr->IsFull ()) + { + perr->AddAddressUnit (*i); + } + else + { + elements.AddInformationElement(perr); + perr->ResetPerr (); + } + } + if (perr->GetNumOfDest () > 0) + { + elements.AddInformationElement(perr); + } + packet->AddHeader (elements); + packet->AddHeader (GetWifiMeshActionHeader ()); + //create 802.11 header: + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + hdr.SetAddr2 (m_parent->GetAddress ()); + hdr.SetAddr3 (m_protocol->GetAddress ()); + if (receivers.size () >= m_protocol->GetUnicastPerrThreshold ()) + { + receivers.clear (); + receivers.push_back (Mac48Address::GetBroadcast ()); + } + //Send Management frame + for (std::vector::const_iterator i = receivers.begin (); i != receivers.end (); i++) + { + hdr.SetAddr1 (*i); + m_stats.txPerr++; + m_stats.txMgt++; + m_stats.txMgtBytes += packet->GetSize (); + m_parent->SendManagementFrame (packet, hdr); + } +} +void +HwmpProtocolMac::InitiatePerr (std::vector failedDestinations, std::vector< + Mac48Address> receivers) +{ + //All duplicates in PERR are checked here, and there is no reason to + //check it at any athoer place + { + std::vector::const_iterator end = receivers.end (); + for (std::vector::const_iterator i = receivers.begin (); i != end; i++) + { + bool should_add = true; + for (std::vector::const_iterator j = m_myPerr.receivers.begin (); j + != m_myPerr.receivers.end (); j++) + { + if ((*i) == (*j)) + { + should_add = false; + } + } + if (should_add) + { + m_myPerr.receivers.push_back (*i); + } + } + } + { + std::vector::const_iterator end = failedDestinations.end (); + for (std::vector::const_iterator i = failedDestinations.begin (); i != end; i++) + { + bool should_add = true; + for (std::vector::const_iterator j = m_myPerr.destinations.begin (); j + != m_myPerr.destinations.end (); j++) + { + if (((*i).destination == (*j).destination) && ((*j).seqnum > (*i).seqnum)) + { + should_add = false; + } + } + if (should_add) + { + m_myPerr.destinations.push_back (*i); + } + } + } + SendMyPerr (); +} +void +HwmpProtocolMac::SendMyPerr () +{ + NS_LOG_FUNCTION_NOARGS (); + if (m_perrTimer.IsRunning ()) + { + return; + } + m_perrTimer = Simulator::Schedule (m_protocol->GetPerrMinInterval (), &HwmpProtocolMac::SendMyPerr, this); + ForwardPerr (m_myPerr.destinations, m_myPerr.receivers); + m_myPerr.destinations.clear (); + m_myPerr.receivers.clear (); +} +uint32_t +HwmpProtocolMac::GetLinkMetric (Mac48Address peerAddress) const +{ + return m_parent->GetLinkMetric (peerAddress); +} +uint16_t +HwmpProtocolMac::GetChannelId () const +{ + return m_parent->GetFrequencyChannel (); +} +HwmpProtocolMac::Statistics::Statistics () : + txPreq (0), rxPreq (0), txPrep (0), rxPrep (0), txPerr (0), rxPerr (0), txMgt (0), txMgtBytes (0), + rxMgt (0), rxMgtBytes (0), txData (0), txDataBytes (0), rxData (0), rxDataBytes (0) +{ +} +void +HwmpProtocolMac::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; +} +void +HwmpProtocolMac::Report (std::ostream & os) const +{ + os << "GetAddress () << "\">" << std::endl; + m_stats.Print (os); + os << "" << std::endl; +} +void +HwmpProtocolMac::ResetStats () +{ + m_stats = Statistics::Statistics (); +} + +} //namespace dot11s +}//namespace ns3 diff --git a/src/devices/mesh/dot11s/hwmp-protocol-mac.h b/src/devices/mesh/dot11s/hwmp-protocol-mac.h new file mode 100644 index 000000000..2bc8c080f --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-protocol-mac.h @@ -0,0 +1,140 @@ +/* -*- 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 + */ + +#ifndef HWMP_STATE_H +#define HWMP_STATE_H + +#include "ns3/mesh-wifi-interface-mac-plugin.h" +#include "ns3/hwmp-protocol.h" + +namespace ns3 { + +class MeshWifiInterfaceMac; + +namespace dot11s { + +class HwmpProtocol; +class WifiMeshActionHeader; +class IePreq; +class IePrep; +class IePerr; + +/** + * \ingroup dot11s + * + * \brief Interface MAC plugin for HWMP -- 802.11s routing protocol + */ +class HwmpProtocolMac : public MeshWifiInterfaceMacPlugin +{ +public: + HwmpProtocolMac (uint32_t, Ptr); + ~HwmpProtocolMac (); + ///\name Inherited from MAC plugin + //\{ + void SetParent (Ptr parent); + bool Receive (Ptr packet, const WifiMacHeader & header); + bool UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, Mac48Address to); + /// Update beacon is empty, because HWMP does not know anything about beacons + void UpdateBeacon (MeshWifiBeacon & beacon) const {}; + //\} + +private: + friend class HwmpProtocol; + ///\returns a path selection action header + static WifiMeshActionHeader GetWifiMeshActionHeader (); + ///\name Intercation with HWMP: + //\{ + void SendPreq (IePreq preq); + void SendPreq (std::vector preq); + void SendPrep (IePrep prep, Mac48Address receiver); + //Forward a peth error + void ForwardPerr (std::vector destinations, std::vector receivers); + // initiate my own path error + void InitiatePerr (std::vector destinations, std::vector receivers); + /** \brief Request a destination. If can not send preq immediately - + * add a destination to exisying PREQ generated by me and stored in + * PREQ queue + * \param dest is the destination to be resolved + * \param originator_seqno is a sequence number that shall be preq originator sequenece number + * \param dst_seqno is a sequence number taken from routing table + */ + void RequestDestination (Mac48Address dest, uint32_t originator_seqno, uint32_t dst_seqno); + //\} + + /// Sends one PREQ when PreqMinInterval after last PREQ expires (if any PREQ exists in rhe queue) + void SendMyPreq (); + void SendMyPerr (); + /// \return metric to HWMP protocol, needed only by metrics to add + //peer as routing entry + uint32_t GetLinkMetric (Mac48Address peerAddress) const; + uint16_t GetChannelId () const; + /// Report statistics + void Report (std::ostream &) const; + void ResetStats (); +private: + Ptr m_parent; + uint32_t m_ifIndex; + Ptr m_protocol; + + ///\name my PREQ and PREQ timer: + //\{ + EventId m_preqTimer; + std::vector m_myPreq; + //\} + ///\name PERR timer and stored path error + //\{ + EventId m_perrTimer; + struct MyPerr { + std::vector destinations; + std::vector receivers; + }; + MyPerr m_myPerr; + ///\name Statistics: + //\{ + struct Statistics + { + uint16_t txPreq; + uint16_t rxPreq; + uint16_t txPrep; + uint16_t rxPrep; + uint16_t txPerr; + uint16_t rxPerr; + uint16_t txMgt; + uint32_t txMgtBytes; + uint16_t rxMgt; + uint32_t rxMgtBytes; + uint16_t txData; + uint32_t txDataBytes; + uint16_t rxData; + uint32_t rxDataBytes; + void Print (std::ostream & os) const; + Statistics (); + }; + Statistics m_stats; + //\} +private: + /// Receive data frame + bool ReceiveData (Ptr packet, const WifiMacHeader & header); + /// Receive action management frame + bool ReceiveAction (Ptr packet, const WifiMacHeader & header); +}; +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/hwmp-protocol.cc b/src/devices/mesh/dot11s/hwmp-protocol.cc new file mode 100644 index 000000000..2187a9cc0 --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-protocol.cc @@ -0,0 +1,1150 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#include "hwmp-protocol.h" +#include "hwmp-protocol-mac.h" +#include "hwmp-tag.h" +#include "hwmp-rtable.h" +#include "ns3/log.h" +#include "ns3/simulator.h" +#include "ns3/packet.h" +#include "ns3/mesh-point-device.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-point-device.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/random-variable.h" +#include "airtime-metric.h" +#include "ie-dot11s-preq.h" +#include "ie-dot11s-prep.h" +#include "ie-dot11s-perr.h" + +NS_LOG_COMPONENT_DEFINE ("HwmpProtocol"); + +namespace ns3 { +namespace dot11s { + +NS_OBJECT_ENSURE_REGISTERED (HwmpProtocol); +TypeId +HwmpProtocol::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::HwmpProtocol") + .SetParent () + .AddConstructor () + .AddAttribute ( "RandomStart", + "Random delay at first proactive PREQ", + TimeValue (Seconds (0.1)), + MakeTimeAccessor ( + &HwmpProtocol::m_randomStart), + MakeTimeChecker () + ) + .AddAttribute ( "MaxQueueSize", + "Maximum number of packets we can store when resolving route", + UintegerValue (255), + MakeUintegerAccessor ( + &HwmpProtocol::m_maxQueueSize), + MakeUintegerChecker (1) + ) + .AddAttribute ( "Dot11MeshHWMPmaxPREQretries", + "Maximum number of retries before we suppose the destination to be unreachable", + UintegerValue (3), + MakeUintegerAccessor ( + &HwmpProtocol::m_dot11MeshHWMPmaxPREQretries), + MakeUintegerChecker (1) + ) + .AddAttribute ( "Dot11MeshHWMPnetDiameterTraversalTime", + "Time we suppose the packet to go from one edge of the network to another", + TimeValue (MicroSeconds (1024*100)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPnetDiameterTraversalTime), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPpreqMinInterval", + "Minimal interval between to successive PREQs", + TimeValue (MicroSeconds (1024*100)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPpreqMinInterval), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPperrMinInterval", + "Minimal interval between to successive PREQs", + TimeValue (MicroSeconds (1024*100)), + MakeTimeAccessor (&HwmpProtocol::m_dot11MeshHWMPperrMinInterval), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPactiveRootTimeout", + "Lifetime of poractive routing information", + TimeValue (MicroSeconds (1024*5000)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPactiveRootTimeout), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPactivePathTimeout", + "Lifetime of reactive routing information", + TimeValue (MicroSeconds (1024*5000)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPactivePathTimeout), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPpathToRootInterval", + "Interval between two successive proactive PREQs", + TimeValue (MicroSeconds (1024*2000)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPpathToRootInterval), + MakeTimeChecker () + ) + .AddAttribute ( "Dot11MeshHWMPrannInterval", + "Lifetime of poractive routing information", + TimeValue (MicroSeconds (1024*5000)), + MakeTimeAccessor ( + &HwmpProtocol::m_dot11MeshHWMPrannInterval), + MakeTimeChecker () + ) + .AddAttribute ( "MaxTtl", + "Initial value of Time To Live field", + UintegerValue (32), + MakeUintegerAccessor ( + &HwmpProtocol::m_maxTtl), + MakeUintegerChecker (2) + ) + .AddAttribute ( "UnicastPerrThreshold", + "Maximum number of PERR receivers, when we send a PERR as a chain of unicasts", + UintegerValue (32), + MakeUintegerAccessor ( + &HwmpProtocol::m_unicastPerrThreshold), + MakeUintegerChecker (1) + ) + .AddAttribute ( "UnicastPreqThreshold", + "Maximum number of PREQ receivers, when we send a PREQ as a chain of unicasts", + UintegerValue (1), + MakeUintegerAccessor ( + &HwmpProtocol::m_unicastPreqThreshold), + MakeUintegerChecker (1) + ) + .AddAttribute ( "UnicastDataThreshold", + "Maximum number ofbroadcast receivers, when we send a broadcast as a chain of unicasts", + UintegerValue (1), + MakeUintegerAccessor ( + &HwmpProtocol::m_unicastDataThreshold), + MakeUintegerChecker (1) + ) + .AddAttribute ( "DoFlag", + "Destination only HWMP flag", + BooleanValue (false), + MakeBooleanAccessor ( + &HwmpProtocol::m_doFlag), + MakeBooleanChecker () + ) + .AddAttribute ( "RfFlag", + "Reply and forward flag", + BooleanValue (true), + MakeBooleanAccessor ( + &HwmpProtocol::m_rfFlag), + MakeBooleanChecker () + ); + return tid; +} + +HwmpProtocol::HwmpProtocol (): + m_dataSeqno (1), + m_hwmpSeqno (1), + m_preqId (0), + m_rtable (CreateObject ()), + m_randomStart(Seconds (0.1)), + m_maxQueueSize (255), + m_dot11MeshHWMPmaxPREQretries (3), + m_dot11MeshHWMPnetDiameterTraversalTime (MicroSeconds (1024*100)), + m_dot11MeshHWMPpreqMinInterval (MicroSeconds (1024*100)), + m_dot11MeshHWMPperrMinInterval (MicroSeconds (1024*100)), + m_dot11MeshHWMPactiveRootTimeout (MicroSeconds (1024*5000)), + m_dot11MeshHWMPactivePathTimeout (MicroSeconds (1024*5000)), + m_dot11MeshHWMPpathToRootInterval (MicroSeconds (1024*2000)), + m_dot11MeshHWMPrannInterval (MicroSeconds (1024*5000)), + m_isRoot (false), + m_maxTtl (32), + m_unicastPerrThreshold (32), + m_unicastPreqThreshold (1), + m_unicastDataThreshold (1), + m_doFlag (false), + m_rfFlag (false) +{ + + if (m_isRoot) + { + SetRoot (); + } +} + +HwmpProtocol::~HwmpProtocol () +{ +} + +void +HwmpProtocol::DoDispose () +{ + for (std::map::iterator i = m_preqTimeouts.begin (); i != m_preqTimeouts.end (); i ++) + { + i->second.Cancel (); + } + m_proactivePreqTimer.Cancel(); + m_preqTimeouts.clear (); + m_lastDataSeqno.clear (); + m_lastHwmpSeqno.clear (); + m_rqueue.clear (); + m_rtable = 0; +} + +bool +HwmpProtocol::RequestRoute ( + uint32_t sourceIface, + const Mac48Address source, + const Mac48Address destination, + Ptr constPacket, + uint16_t protocolType, //ethrnet 'Protocol' field + MeshL2RoutingProtocol::RouteReplyCallback routeReply +) +{ + Ptr packet = constPacket->Copy (); + HwmpTag tag; + if (sourceIface == GetMeshPoint ()->GetIfIndex()) + { + // packet from level 3 + if (packet->PeekPacketTag (tag)) + { + NS_FATAL_ERROR ("HWMP tag has come with a packet from upper layer. This must not occur..."); + } + //Filling TAG: + if (destination == Mac48Address::GetBroadcast ()) + { + tag.SetSeqno (m_dataSeqno++); + } + tag.SetTtl (m_maxTtl); + } + else + { + if (!packet->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("HWMP tag is supposed to be here at this point."); + } + tag.DecrementTtl (); + if (tag.GetTtl () == 0) + { + m_stats.droppedTtl ++; + return false; + } + } + if (destination == Mac48Address::GetBroadcast ()) + { + m_stats.txBroadcast ++; + m_stats.txBytes += packet->GetSize (); + //channel IDs where we have already sent broadcast: + std::vector channels; + for (HwmpProtocolMacMap::const_iterator plugin = m_interfaces.begin (); plugin != m_interfaces.end (); plugin ++) + { + bool shouldSend = true; + for (std::vector::const_iterator chan = channels.begin (); chan != channels.end (); chan ++) + { + if ((*chan) == plugin->second->GetChannelId ()) + { + shouldSend = false; + } + } + if (!shouldSend) + { + continue; + } + channels.push_back (plugin->second->GetChannelId ()); + std::vector receivers = GetBroadcastReceivers (plugin->first); + for (std::vector::const_iterator i = receivers.begin (); i != receivers.end (); i ++) + { + Ptr packetCopy = packet->Copy(); + tag.SetAddress (*i); + packetCopy->AddPacketTag (tag); + routeReply (true, packetCopy, source, destination, protocolType, plugin->first); + } + } + } + else + { + return ForwardUnicast (sourceIface, source, destination, packet, protocolType, routeReply, tag.GetTtl ()); + } + return true; +} +bool +HwmpProtocol::RemoveRoutingStuff (uint32_t fromIface, const Mac48Address source, + const Mac48Address destination, Ptr packet, uint16_t& protocolType) +{ + HwmpTag tag; + if (!packet->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("HWMP tag must exist when packet received from the network"); + } + return true; +} +bool +HwmpProtocol::ForwardUnicast (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, + Ptr packet, uint16_t protocolType, RouteReplyCallback routeReply, uint32_t ttl) +{ + NS_ASSERT(destination != Mac48Address::GetBroadcast ()); + HwmpRtable::LookupResult result = m_rtable->LookupReactive (destination); + NS_LOG_DEBUG("Requested src = "<LookupReactiveExpired (destination); + //1. Lookup expired reactive path. If exists - start path error + // procedure towards a next hop of this path + //2. If there was no reactive path, we lookup expired proactive + // path. If exist - start path error procedure towards path to + // root + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + result = m_rtable->LookupProactiveExpired (); + } + if (result.retransmitter != Mac48Address::GetBroadcast ()) + { + std::vector destinations = m_rtable->GetUnreachableDestinations (result.retransmitter); + InitiatePathError (MakePathError (destinations)); + } + m_stats.totalDropped ++; + return false; + } + //Request a destination: + result = m_rtable->LookupReactiveExpired (destination); + if (ShouldSendPreq (destination)) + { + uint32_t originator_seqno = GetNextHwmpSeqno (); + uint32_t dst_seqno = 0; + if (result.retransmitter != Mac48Address::GetBroadcast ()) + { + dst_seqno = result.seqnum; + } + m_stats.initiatedPreq ++; + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + i->second->RequestDestination (destination, originator_seqno, dst_seqno); + } + } + QueuedPacket pkt; + pkt.pkt = packet; + pkt.dst = destination; + pkt.src = source; + pkt.protocol = protocolType; + pkt.reply = routeReply; + pkt.inInterface = sourceIface; + if (QueuePacket (pkt)) + { + m_stats.totalQueued ++; + return true; + } + else + { + m_stats.totalDropped ++; + return false; + } +} +void +HwmpProtocol::ReceivePreq (IePreq preq, Mac48Address from, uint32_t interface, Mac48Address fromMp, uint32_t metric) +{ + preq.IncrementMetric (metric); + //acceptance cretirea: + std::map::const_iterator i = m_lastHwmpSeqno.find (preq.GetOriginatorAddress()); + if (i == m_lastHwmpSeqno.end ()) + { + m_lastHwmpSeqno[preq.GetOriginatorAddress ()] = preq.GetOriginatorSeqNumber (); + m_lastHwmpMetric[preq.GetOriginatorAddress ()] = preq.GetMetric (); + } + else + { + if ((int32_t)(i->second - preq.GetOriginatorSeqNumber ()) > 0) + { + return; + } + if (i->second == preq.GetOriginatorSeqNumber ()) + { + //find metric + std::map::const_iterator j = m_lastHwmpMetric.find (preq.GetOriginatorAddress()); + NS_ASSERT (j != m_lastHwmpSeqno.end ()); + if (j->second <= preq.GetMetric ()) + { + return; + } + } + m_lastHwmpSeqno[preq.GetOriginatorAddress ()] = preq.GetOriginatorSeqNumber (); + m_lastHwmpMetric[preq.GetOriginatorAddress ()] = preq.GetMetric (); + } + NS_LOG_DEBUG("I am " << GetAddress () << "Accepted preq from address" << from << ", preq:" << preq); + std::vector > destinations = preq.GetDestinationList (); + //Add reactive path to originator: + if ( + ((m_rtable->LookupReactive (preq.GetOriginatorAddress ())).retransmitter == Mac48Address::GetBroadcast ()) || + ((m_rtable->LookupReactive (preq.GetOriginatorAddress ())).metric > preq.GetMetric ()) + ) + { + m_rtable->AddReactivePath ( + preq.GetOriginatorAddress (), + from, + interface, + preq.GetMetric (), + MicroSeconds (preq.GetLifetime () * 1024), + preq.GetOriginatorSeqNumber () + ); + ReactivePathResolved (preq.GetOriginatorAddress ()); + } + //Add reactive path for precursor: + if ( + ((m_rtable->LookupReactive (fromMp)).retransmitter == Mac48Address::GetBroadcast ()) || + ((m_rtable->LookupReactive (fromMp)).metric > preq.GetMetric ()) + ) + { + m_rtable->AddReactivePath ( + fromMp, + from, + interface, + metric, + MicroSeconds (preq.GetLifetime () * 1024), + preq.GetOriginatorSeqNumber () + ); + ReactivePathResolved (fromMp); + } + for (std::vector >::const_iterator i = destinations.begin (); i != destinations.end (); i++) + { + if ((*i)->GetDestinationAddress () == Mac48Address::GetBroadcast()) + { + //only proactive PREQ contains destination + //address as broadcast! Proactive preq MUST + //have destination count equal to 1 and + //per destination flags DO and RF + NS_ASSERT (preq.GetDestCount() == 1); + NS_ASSERT (((*i)->IsDo ()) && ((*i)->IsRf ())); + //Add proactive path only if it is the better then existed + //before + if ( + ((m_rtable->LookupProactive ()).retransmitter == Mac48Address::GetBroadcast ()) || + ((m_rtable->LookupProactive ()).metric > preq.GetMetric ()) + ) + { + m_rtable->AddProactivePath ( + preq.GetMetric (), + preq.GetOriginatorAddress (), + from, + interface, + MicroSeconds (preq.GetLifetime () * 1024), + preq.GetOriginatorSeqNumber () + ); + ProactivePathResolved (); + } + if (!preq.IsNeedNotPrep ()) + { + SendPrep ( + GetAddress (), + preq.GetOriginatorAddress (), + from, + preq.GetMetric (), + preq.GetOriginatorSeqNumber (), + GetNextHwmpSeqno (), + preq.GetLifetime (), + interface + ); + } + break; + } + if ((*i)->GetDestinationAddress () == GetAddress ()) + { + SendPrep ( + GetAddress (), + preq.GetOriginatorAddress (), + from, + (uint32_t)0, + preq.GetOriginatorSeqNumber (), + GetNextHwmpSeqno (), + preq.GetLifetime (), + interface + ); + NS_ASSERT(m_rtable->LookupReactive (preq.GetOriginatorAddress ()).retransmitter != Mac48Address::GetBroadcast ()); + preq.DelDestinationAddressElement ((*i)->GetDestinationAddress ()); + continue; + } + //check if can answer: + HwmpRtable::LookupResult result = m_rtable->LookupReactive ((*i)->GetDestinationAddress ()); + if ((! ((*i)->IsDo ())) && (result.retransmitter != Mac48Address::GetBroadcast ())) + { + //have a valid information and can answer + //!NB: If there is information from peer - set lifetime as + //we have got from PREQ, and set the rest lifetime of the + //route if the information is correct + uint32_t lifetime = result.lifetime.GetMicroSeconds () / 1024; + if ((lifetime > 0) && ((int32_t)(result.seqnum - (*i)->GetDestSeqNumber ()) >= 0)) + { + SendPrep ( + (*i)->GetDestinationAddress (), + preq.GetOriginatorAddress (), + from, + result.metric, + preq.GetOriginatorSeqNumber (), + result.seqnum, + lifetime, + interface + ); + if ((*i)->IsRf ()) + { + (*i)->SetFlags (true, false, (*i)->IsUsn ()); //DO = 1, RF = 0 + } + else + { + preq.DelDestinationAddressElement ((*i)->GetDestinationAddress ()); + continue; + } + } + } + } + //check if must retransmit: + if (preq.GetDestCount () == 0) + { + return; + } + //Forward PREQ to all interfaces: + NS_LOG_DEBUG("I am " << GetAddress () << "retransmitting PREQ:" << preq); + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + i->second->SendPreq (preq); + } +} +void +HwmpProtocol::ReceivePrep (IePrep prep, Mac48Address from, uint32_t interface, Mac48Address fromMp, uint32_t metric) +{ + prep.IncrementMetric (metric); + //acceptance cretirea: + std::map::const_iterator i = m_lastHwmpSeqno.find (prep.GetOriginatorAddress ()); + if (i == m_lastHwmpSeqno.end ()) + { + m_lastHwmpSeqno[prep.GetOriginatorAddress ()] = prep.GetOriginatorSeqNumber (); + } + else + { + if ((int32_t)(i->second - prep.GetOriginatorSeqNumber ()) > 0) + { + return; + } + else + { + m_lastHwmpSeqno[prep.GetOriginatorAddress ()] = prep.GetOriginatorSeqNumber (); + } + } + //update routing info + //Now add a path to destination and add precursor to source + NS_LOG_DEBUG("I am " << GetAddress () << ", received prep from " << prep.GetOriginatorAddress () << ", receiver was:" << from); + HwmpRtable::LookupResult result = m_rtable->LookupReactive (prep.GetDestinationAddress ()); + //Add a reactive path only if it is better than existing: + if ( + ((m_rtable->LookupReactive (prep.GetOriginatorAddress ())).retransmitter == Mac48Address::GetBroadcast ()) || + ((m_rtable->LookupReactive (prep.GetOriginatorAddress ())).metric > prep.GetMetric ()) + ) + { + m_rtable->AddReactivePath ( + prep.GetOriginatorAddress (), + from, + interface, + prep.GetMetric (), + MicroSeconds(prep.GetLifetime () * 1024), + prep.GetOriginatorSeqNumber ()); + m_rtable->AddPrecursor (prep.GetDestinationAddress (), interface, from); + if (result.retransmitter != Mac48Address::GetBroadcast ()) + { + m_rtable->AddPrecursor (prep.GetOriginatorAddress (), interface, result.retransmitter); + } + ReactivePathResolved (prep.GetOriginatorAddress ()); + } + if ( + ((m_rtable->LookupReactive (fromMp)).retransmitter == Mac48Address::GetBroadcast ()) || + ((m_rtable->LookupReactive (fromMp)).metric > prep.GetMetric ()) + ) + { + m_rtable->AddReactivePath ( + fromMp, + from, + interface, + metric, + MicroSeconds(prep.GetLifetime () * 1024), + prep.GetOriginatorSeqNumber ()); + ReactivePathResolved (fromMp); + } + if (prep.GetDestinationAddress () == GetAddress ()) + { + NS_LOG_DEBUG("I am "<LookupProactive (); + } + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + return; + } + //Forward PREP + HwmpProtocolMacMap::const_iterator prep_sender = m_interfaces.find (result.ifIndex); + NS_ASSERT (prep_sender != m_interfaces.end ()); + prep_sender->second->SendPrep (prep, result.retransmitter); +} +void +HwmpProtocol::ReceivePerr (std::vector destinations, Mac48Address from, uint32_t interface, Mac48Address fromMp) +{ + //Acceptance cretirea: + NS_LOG_DEBUG ("I am "< retval; + HwmpRtable::LookupResult result; + for (unsigned int i = 0; i < destinations.size (); i ++) + { + result = m_rtable->LookupReactiveExpired (destinations[i].destination); + if (!( + (result.retransmitter != from) || + (result.ifIndex != interface) || + ((int32_t)(result.seqnum - destinations[i].seqnum) > 0) + )) + { + retval.push_back (destinations[i]); + } + } + if (retval.size () == 0) + { + return; + } + ForwardPathError (MakePathError (retval)); +} +void +HwmpProtocol::SendPrep ( + Mac48Address src, + Mac48Address dst, + Mac48Address retransmitter, + uint32_t initMetric, + uint32_t originatorDsn, + uint32_t destinationSN, + uint32_t lifetime, + uint32_t interface) +{ + IePrep prep; + prep.SetHopcount (0); + prep.SetTtl (m_maxTtl); + prep.SetDestinationAddress (dst); + prep.SetDestinationSeqNumber (destinationSN); + prep.SetLifetime (lifetime); + prep.SetMetric (0); + prep.SetOriginatorAddress (src); + prep.SetOriginatorSeqNumber (originatorDsn); + HwmpProtocolMacMap::const_iterator prep_sender = m_interfaces.find (interface); + NS_ASSERT(prep_sender != m_interfaces.end ()); + prep_sender->second->SendPrep (prep, retransmitter); + m_stats.initiatedPrep ++; +} +bool +HwmpProtocol::Install (Ptr mp) +{ + m_mp = mp; + std::vector > interfaces = mp->GetInterfaces (); + for (std::vector >::const_iterator i = interfaces.begin (); i != interfaces.end (); i++) + { + // Checking for compatible net device + Ptr wifiNetDev = (*i)->GetObject (); + if (wifiNetDev == 0) + { + return false; + } + Ptr mac = wifiNetDev->GetMac ()->GetObject (); + if (mac == 0) + { + return false; + } + // Installing plugins: + Ptr hwmpMac = Create (wifiNetDev->GetIfIndex (), this); + m_interfaces[wifiNetDev->GetIfIndex ()] = hwmpMac; + mac->InstallPlugin (hwmpMac); + //Installing airtime link metric: + Ptr metric = CreateObject (); + mac->SetLinkMetricCallback (MakeCallback (&AirtimeLinkMetricCalculator::CalculateMetric, metric)); + } + mp->SetRoutingProtocol (this); + // Mesh point aggregates all installed protocols + mp->AggregateObject (this); + m_address = Mac48Address::ConvertFrom (mp->GetAddress ());// address; + return true; +} +void +HwmpProtocol::PeerLinkStatus(Mac48Address meshPointAddress, Mac48Address peerAddress, uint32_t interface, bool status) +{ + if (status) + { + return; + } + std::vector destinations = m_rtable->GetUnreachableDestinations (peerAddress); + InitiatePathError (MakePathError (destinations)); +} +void +HwmpProtocol::SetNeighboursCallback (Callback, uint32_t> cb) +{ + m_neighboursCallback = cb; +} +bool +HwmpProtocol::DropDataFrame (uint32_t seqno, Mac48Address source) +{ + if (source == GetAddress ()) + { + return true; + } + std::map >::const_iterator i = m_lastDataSeqno.find (source); + if (i == m_lastDataSeqno.end ()) + { + m_lastDataSeqno[source] = seqno; + } + else + { + if ((int32_t)(i->second - seqno) >= 0) + { + return true; + } + m_lastDataSeqno[source] = seqno; + } + return false; +} +HwmpProtocol::PathError +HwmpProtocol::MakePathError (std::vector destinations) +{ + PathError retval; + //HwmpRtable increments a sequence number as written in 11B.9.7.2 + retval.receivers = GetPerrReceivers (destinations); + if (retval.receivers.size () == 0) + { + return retval; + } + m_stats.initiatedPerr ++; + for (unsigned int i = 0; i < destinations.size (); i ++) + { + retval.destinations.push_back (destinations[i]); + m_rtable->DeleteReactivePath (destinations[i].destination); + } + return retval; +} +void +HwmpProtocol::InitiatePathError(PathError perr) +{ + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + std::vector receivers_for_interface; + for (unsigned int j = 0; j < perr.receivers.size (); j ++) + { + if (i->first == perr.receivers[j].first) + { + receivers_for_interface.push_back (perr.receivers[j].second); + } + } + i->second->InitiatePerr (perr.destinations, receivers_for_interface); + } +} +void +HwmpProtocol::ForwardPathError(PathError perr) +{ + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + std::vector receivers_for_interface; + for (unsigned int j = 0; j < perr.receivers.size (); j ++) + { + if (i->first == perr.receivers[j].first) + { + receivers_for_interface.push_back (perr.receivers[j].second); + } + } + i->second->ForwardPerr (perr.destinations, receivers_for_interface); + } +} + +std::vector > +HwmpProtocol::GetPerrReceivers (std::vector failedDest) +{ + HwmpRtable::PrecursorList retval; + for (unsigned int i = 0; i < failedDest.size (); i ++) + { + HwmpRtable::PrecursorList precursors = m_rtable->GetPrecursors (failedDest[i].destination); + m_rtable->DeleteReactivePath (failedDest[i].destination); + m_rtable->DeleteProactivePath (failedDest[i].destination); + for (unsigned int j = 0; j < precursors.size (); j ++) + { + retval.push_back (precursors[j]); + } + } + //Check if we have dublicates in retval and precursors: + for (unsigned int i = 0; i < retval.size (); i ++) + { + for (unsigned int j = i+1; j < retval.size (); j ++) + { + if (retval[i].second == retval[j].second) + { + retval.erase (retval.begin () + j); + } + } + } + return retval; +} +std::vector +HwmpProtocol::GetPreqReceivers (uint32_t interface) +{ + std::vector retval; + if (!m_neighboursCallback.IsNull ()) + { + retval = m_neighboursCallback (interface); + } + if ((retval.size () >= m_unicastPreqThreshold) || (retval.size () == 0)) + { + retval.clear (); + retval.push_back (Mac48Address::GetBroadcast ()); + } + return retval; +} +std::vector +HwmpProtocol::GetBroadcastReceivers (uint32_t interface) +{ + std::vector retval; + if (!m_neighboursCallback.IsNull ()) + { + retval = m_neighboursCallback (interface); + } + if ((retval.size () >= m_unicastDataThreshold) || (retval.size () == 0)) + { + retval.clear (); + retval.push_back (Mac48Address::GetBroadcast ()); + } + return retval; +} + +bool +HwmpProtocol::QueuePacket (QueuedPacket packet) +{ + if (m_rqueue.size () > m_maxQueueSize) + { + return false; + } + m_rqueue.push_back (packet); + return true; +} + +HwmpProtocol::QueuedPacket +HwmpProtocol::DequeueFirstPacketByDst (Mac48Address dst) +{ + QueuedPacket retval; + retval.pkt = 0; + for (std::vector::iterator i = m_rqueue.begin (); i != m_rqueue.end (); i++) + { + if ((*i).dst == dst) + { + retval = (*i); + m_rqueue.erase (i); + break; + } + } + return retval; +} + +HwmpProtocol::QueuedPacket +HwmpProtocol::DequeueFirstPacket () +{ + QueuedPacket retval; + retval.pkt = 0; + if (m_rqueue.size () != 0) + { + retval = m_rqueue[0]; + m_rqueue.erase (m_rqueue.begin ()); + } + return retval; +} + +void +HwmpProtocol::ReactivePathResolved (Mac48Address dst) +{ + HwmpRtable::LookupResult result = m_rtable->LookupReactive (dst); + NS_ASSERT(result.retransmitter != Mac48Address::GetBroadcast ()); + //Send all packets stored for this destination + QueuedPacket packet = DequeueFirstPacketByDst (dst); + while (packet.pkt != 0) + { + //set RA tag for retransmitter: + HwmpTag tag; + packet.pkt->RemovePacketTag (tag); + tag.SetAddress (result.retransmitter); + packet.pkt->AddPacketTag (tag); + m_stats.txUnicast ++; + m_stats.txBytes += packet.pkt->GetSize (); + packet.reply (true, packet.pkt, packet.src, packet.dst, packet.protocol, result.ifIndex); + + packet = DequeueFirstPacketByDst (dst); + } +} +void +HwmpProtocol::ProactivePathResolved () +{ + //send all packets to root + HwmpRtable::LookupResult result = m_rtable->LookupProactive (); + NS_ASSERT (result.retransmitter != Mac48Address::GetBroadcast ()); + QueuedPacket packet = DequeueFirstPacket (); + while (packet.pkt != 0) + { + //set RA tag for retransmitter: + HwmpTag tag; + if (!packet.pkt->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("HWMP tag must be present at this point"); + } + tag.SetAddress (result.retransmitter); + packet.pkt->AddPacketTag (tag); + m_stats.txUnicast ++; + m_stats.txBytes += packet.pkt->GetSize (); + packet.reply (true, packet.pkt, packet.src, packet.dst, packet.protocol, result.ifIndex); + + packet = DequeueFirstPacket (); + } +} + +bool +HwmpProtocol::ShouldSendPreq (Mac48Address dst) +{ + std::map::const_iterator i = m_preqTimeouts.find (dst); + if (i == m_preqTimeouts.end ()) + { + m_preqTimeouts[dst] = Simulator::Schedule ( + m_dot11MeshHWMPnetDiameterTraversalTime * Scalar (2), + &HwmpProtocol::RetryPathDiscovery, this, dst, 1); + return true; + } + return false; +} +void +HwmpProtocol::RetryPathDiscovery (Mac48Address dst, uint8_t numOfRetry) +{ + HwmpRtable::LookupResult result = m_rtable->LookupReactive (dst); + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + result = m_rtable->LookupProactive (); + } + if (result.retransmitter != Mac48Address::GetBroadcast ()) + { + std::map::iterator i = m_preqTimeouts.find (dst); + NS_ASSERT (i != m_preqTimeouts.end ()); + m_preqTimeouts.erase (i); + return; + } + numOfRetry++; + if (numOfRetry >= m_dot11MeshHWMPmaxPREQretries) + { + QueuedPacket packet = DequeueFirstPacketByDst (dst); + //purge queue and delete entry from retryDatabase + while (packet.pkt != 0) + { + m_stats.totalDropped ++; + packet.reply (false, packet.pkt, packet.src, packet.dst, packet.protocol, HwmpRtable::MAX_METRIC); + packet = DequeueFirstPacketByDst (dst); + } + std::map::iterator i = m_preqTimeouts.find (dst); + NS_ASSERT (i != m_preqTimeouts.end ()); + m_preqTimeouts.erase (i); + return; + } + uint32_t originator_seqno = GetNextHwmpSeqno (); + uint32_t dst_seqno = m_rtable->LookupReactiveExpired (dst).seqnum; + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + i->second->RequestDestination (dst, originator_seqno, dst_seqno); + } + m_preqTimeouts[dst] = Simulator::Schedule ( + Scalar (2 * (numOfRetry + 1)) * m_dot11MeshHWMPnetDiameterTraversalTime, + &HwmpProtocol::RetryPathDiscovery, this, dst, numOfRetry); +} +//Proactive PREQ routines: +void +HwmpProtocol::SetRoot () +{ + UniformVariable coefficient (0.0, m_randomStart.GetSeconds()); + Time randomStart = Seconds (coefficient.GetValue ()); + m_proactivePreqTimer = Simulator::Schedule (randomStart, &HwmpProtocol::SendProactivePreq, this); + NS_LOG_DEBUG ("ROOT IS: " << m_address); + SendProactivePreq (); + m_isRoot = true; +} +void +HwmpProtocol::UnsetRoot () +{ + m_proactivePreqTimer.Cancel (); +} +void +HwmpProtocol::SendProactivePreq () +{ + IePreq preq; + //By default: must answer + preq.SetHopcount (0); + preq.SetTTL (m_maxTtl); + preq.SetLifetime (m_dot11MeshHWMPactiveRootTimeout.GetMicroSeconds () /1024); + //\attention: do not forget to set originator address, sequence + //number and preq ID in HWMP-MAC plugin + preq.AddDestinationAddressElement (true, true, Mac48Address::GetBroadcast (), 0); + preq.SetOriginatorAddress (GetAddress ()); + preq.SetPreqID (GetNextPreqId ()); + preq.SetOriginatorSeqNumber (GetNextHwmpSeqno ()); + for (HwmpProtocolMacMap::const_iterator i = m_interfaces.begin (); i != m_interfaces.end (); i ++) + { + i->second->SendPreq (preq); + } + m_proactivePreqTimer = Simulator::Schedule (m_dot11MeshHWMPpathToRootInterval, &HwmpProtocol::SendProactivePreq, this); +} +bool +HwmpProtocol::GetDoFlag () +{ + return m_doFlag; +} +bool +HwmpProtocol::GetRfFlag () +{ + return m_rfFlag; +} +Time +HwmpProtocol::GetPreqMinInterval () +{ + return m_dot11MeshHWMPpreqMinInterval; +} +Time +HwmpProtocol::GetPerrMinInterval () +{ + return m_dot11MeshHWMPperrMinInterval; +} +uint8_t +HwmpProtocol::GetMaxTtl () +{ + return m_maxTtl; +} +uint32_t +HwmpProtocol::GetNextPreqId () +{ + m_preqId ++; + return m_preqId; +} +uint32_t +HwmpProtocol::GetNextHwmpSeqno () +{ + m_hwmpSeqno ++; + return m_hwmpSeqno; +} +uint32_t +HwmpProtocol::GetActivePathLifetime () +{ + return m_dot11MeshHWMPactivePathTimeout.GetMicroSeconds () / 1024; +} +uint8_t +HwmpProtocol::GetUnicastPerrThreshold () +{ + return m_unicastPerrThreshold; +} +Mac48Address +HwmpProtocol::GetAddress () +{ + return m_address; +} +//Statistics: +HwmpProtocol::Statistics::Statistics () : + txUnicast (0), + txBroadcast (0), + txBytes (0), + droppedTtl (0), + totalQueued (0), + totalDropped (0), + initiatedPreq (0), + initiatedPrep (0), + initiatedPerr (0) +{} +void HwmpProtocol::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; + m_stats.Print (os); + for (HwmpProtocolMacMap::const_iterator plugin = m_interfaces.begin (); plugin != m_interfaces.end (); plugin ++) + { + plugin->second->Report (os); + } + os << "" << std::endl; +} +void +HwmpProtocol::ResetStats () +{ + m_stats = Statistics::Statistics (); + for (HwmpProtocolMacMap::const_iterator plugin = m_interfaces.begin (); plugin != m_interfaces.end (); plugin ++) + { + plugin->second->ResetStats (); + } +} +HwmpProtocol::QueuedPacket::QueuedPacket () : + pkt (0), + protocol (0), + inInterface (0) +{} +} //namespace dot11s +} //namespace ns3 diff --git a/src/devices/mesh/dot11s/hwmp-protocol.h b/src/devices/mesh/dot11s/hwmp-protocol.h new file mode 100644 index 000000000..f451468bc --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-protocol.h @@ -0,0 +1,265 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#ifndef HWMP_PROTOCOL_H +#define HWMP_PROTOCOL_H + +#include "ns3/mesh-l2-routing-protocol.h" +#include "ns3/nstime.h" +#include "ns3/event-id.h" +#include +#include + +namespace ns3 { +class MeshPointDevice; +class Packet; +class Mac48Address; +namespace dot11s { +class HwmpProtocolMac; +class HwmpRtable; +class IePerr; +class IePreq; +class IePrep; +/** + * \ingroup dot11s + * + * \brief Hybrid wireless mesh protocol -- a routing protocol of IEEE 802.11s draft. + */ +class HwmpProtocol : public MeshL2RoutingProtocol +{ +public: + static TypeId GetTypeId (); + HwmpProtocol (); + ~HwmpProtocol (); + void DoDispose (); + /** + * \brief structure of unreachable destination - address and sequence number + */ + typedef struct + { + Mac48Address destination; + uint32_t seqnum; + } FailedDestination; + /// Route request, inherited from MeshL2RoutingProtocol + bool RequestRoute (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, + Ptr packet, uint16_t protocolType, RouteReplyCallback routeReply); + /// Cleanup packet from all tags + bool RemoveRoutingStuff (uint32_t fromIface, const Mac48Address source, + const Mac48Address destination, Ptr packet, uint16_t& protocolType); + /** + * \brief Install HWMP on given mesh point. + * + * Installing protocol cause installing its interface MAC plugins. + * + * Also MP aggregates all installed protocols, HWMP protocol can be accessed + * via MeshPointDevice::GetObject(); + */ + bool Install (Ptr); + void PeerLinkStatus (Mac48Address meshPontAddress, Mac48Address peerAddress, uint32_t interface,bool status); + ///\brief This callback is used to obtain active neighbours on a given interface + ///\param cb is a callback, which returns a list of addresses on given interface (uint32_t) + void SetNeighboursCallback (Callback, uint32_t> cb); + ///\name Proactive PREQ mechanism: + ///\{ + void SetRoot (); + void UnsetRoot (); + ///\} + ///\brief Statistics: + void Report (std::ostream &) const; + void ResetStats (); +private: + friend class HwmpProtocolMac; + /** + * \brief Structure of path error: IePerr and list of receivers: + * interfaces and MAC address + */ + struct PathError + { + std::vector destinations; ///< destination list: Mac48Address and sequence number + std::vector > receivers; ///< list of PathError receivrs (in case of unicast PERR) + }; + /// Packet waiting its routing information + struct QueuedPacket + { + Ptr pkt; ///< the packet + Mac48Address src; ///< src address + Mac48Address dst; ///< dst address + uint16_t protocol; ///< protocol number + uint32_t inInterface; ///< incoming device interface ID. (if packet has come from upper layers, this is Mesh point ID) + RouteReplyCallback reply; ///< how to reply + + QueuedPacket (); + }; + typedef std::map > HwmpProtocolMacMap; + /// Like RequestRoute, but for unicast packets + bool ForwardUnicast (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, + Ptr packet, uint16_t protocolType, RouteReplyCallback routeReply, uint32_t ttl); + + ///\name Interaction with HWMP MAC plugin + //\{ + void ReceivePreq (IePreq preq, Mac48Address from, uint32_t interface, Mac48Address fromMp, uint32_t metric); + void ReceivePrep (IePrep prep, Mac48Address from, uint32_t interface, Mac48Address fromMp, uint32_t metric); + void ReceivePerr (std::vector, Mac48Address from, uint32_t interface, Mac48Address fromMp); + void SendPrep ( + Mac48Address src, + Mac48Address dst, + Mac48Address retransmitter, + uint32_t initMetric, + uint32_t originatorDsn, + uint32_t destinationSN, + uint32_t lifetime, + uint32_t interface); + /** + * \brief forms a path error information element when list of destination fails on a given interface + * \attention removes all entries from routing table! + */ + PathError MakePathError (std::vector destinations); + ///\brief Forwards a received path error + void ForwardPathError (PathError perr); + ///\brief Pasess a selg-generated PERR to interface-plugin + void InitiatePathError (PathError perr); + /// \return list of addresses where a PERR should be sent to + std::vector > GetPerrReceivers (std::vector failedDest); + + /// \return list of addresses where a PERR should be sent to + std::vector GetPreqReceivers (uint32_t interface); + /// \return list of addresses where a broadcast should be + //retransmitted + std::vector GetBroadcastReceivers (uint32_t interface); + /** + * \brief MAC-plugin asks whether the frame can be dropped. Protocol automatically updates seqno. + * + * \return true if frame can be dropped + * \param seqno is the sequence number of source + * \param source is the source address + */ + bool DropDataFrame (uint32_t seqno, Mac48Address source); + //\} + ///\name Methods related to Queue/Dequeue procedures + ///\{ + bool QueuePacket (QueuedPacket packet); + QueuedPacket DequeueFirstPacketByDst (Mac48Address dst); + QueuedPacket DequeueFirstPacket (); + void ReactivePathResolved (Mac48Address dst); + void ProactivePathResolved (); + ///\} + ///\name Methods responsible for path discovery retry procedure: + ///\{ + /** + * \brief checks when the last path discovery procedure was started for a given destination. + * + * If the retry counter has not achieved the maximum level - preq should not be sent + */ + bool ShouldSendPreq (Mac48Address dst); + + /** + * \brief Generates PREQ retry when retry timeout has expired and route is still unresolved. + * + * When PREQ retry has achieved the maximum level - retry mechanism should be canceled + */ + void RetryPathDiscovery (Mac48Address dst, uint8_t numOfRetry); + /// Proactive Preq routines: + void SendProactivePreq (); + ///\} + ///\return address of MeshPointDevice + Mac48Address GetAddress (); + ///\name Methods needed by HwmpMacLugin to access protocol parameters: + ///\{ + bool GetDoFlag (); + bool GetRfFlag (); + Time GetPreqMinInterval (); + Time GetPerrMinInterval (); + uint8_t GetMaxTtl (); + uint32_t GetNextPreqId (); + uint32_t GetNextHwmpSeqno (); + uint32_t GetActivePathLifetime (); + uint8_t GetUnicastPerrThreshold (); + ///\} +private: + ///\name Statistics: + ///\{ + struct Statistics + { + uint16_t txUnicast; + uint16_t txBroadcast; + uint32_t txBytes; + uint16_t droppedTtl; + uint16_t totalQueued; + uint16_t totalDropped; + uint16_t initiatedPreq; + uint16_t initiatedPrep; + uint16_t initiatedPerr; + + void Print (std::ostream & os) const; + Statistics (); + }; + Statistics m_stats; + ///\} + HwmpProtocolMacMap m_interfaces; + Mac48Address m_address; + uint32_t m_dataSeqno; + uint32_t m_hwmpSeqno; + uint32_t m_preqId; + ///\name Sequence number filters + ///\{ + /// Data sequence number database + std::map m_lastDataSeqno; + /// DSN databse + std::map m_lastHwmpSeqno; + /// Metric database + std::map m_lastHwmpMetric; + ///\} + + /// Routing table + Ptr m_rtable; + + ///\name Timers: + ///\{ + std::map m_preqTimeouts; + EventId m_proactivePreqTimer; + /// Random start in Proactive PREQ propagation + Time m_randomStart; + ///\} + /// Packet Queue + std::vector m_rqueue; + ///\name HWMP-protocol parameters (attributes of GetTypeId) + ///\{ + uint16_t m_maxQueueSize; + uint8_t m_dot11MeshHWMPmaxPREQretries; + Time m_dot11MeshHWMPnetDiameterTraversalTime; + Time m_dot11MeshHWMPpreqMinInterval; + Time m_dot11MeshHWMPperrMinInterval; + Time m_dot11MeshHWMPactiveRootTimeout; + Time m_dot11MeshHWMPactivePathTimeout; + Time m_dot11MeshHWMPpathToRootInterval; + Time m_dot11MeshHWMPrannInterval; + bool m_isRoot; + uint8_t m_maxTtl; + uint8_t m_unicastPerrThreshold; + uint8_t m_unicastPreqThreshold; + uint8_t m_unicastDataThreshold; + bool m_doFlag; + bool m_rfFlag; + ///\} + Callback , uint32_t> m_neighboursCallback; +}; +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/hwmp-rtable.cc b/src/devices/mesh/dot11s/hwmp-rtable.cc new file mode 100644 index 000000000..9348ab63d --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-rtable.cc @@ -0,0 +1,393 @@ +/* -*- 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 + */ + +#include "ns3/object.h" +#include "ns3/assert.h" +#include "ns3/simulator.h" +#include "ns3/test.h" +#include "ns3/log.h" + +#include "hwmp-rtable.h" + +namespace ns3 { +namespace dot11s { + +NS_LOG_COMPONENT_DEFINE ("HwmpRtable"); + +NS_OBJECT_ENSURE_REGISTERED (HwmpRtable); + +TypeId +HwmpRtable::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::HwmpRtable") + .SetParent () + .AddConstructor (); + return tid; +} +HwmpRtable::HwmpRtable () +{ + DeleteProactivePath (); +} +HwmpRtable::~HwmpRtable () +{ +} +void +HwmpRtable::DoDispose () +{ + m_routes.clear (); +} +void +HwmpRtable::AddReactivePath (Mac48Address destination, Mac48Address retransmitter, uint32_t interface, + uint32_t metric, Time lifetime, uint32_t seqnum) +{ + std::map::iterator i = m_routes.find (destination); + if (i == m_routes.end ()) + { + ReactiveRoute newroute; + m_routes[destination] = newroute; + } + i = m_routes.find (destination); + NS_ASSERT (i != m_routes.end ()); + i->second.retransmitter = retransmitter; + i->second.interface = interface; + i->second.metric = metric; + i->second.whenExpire = Simulator::Now () + lifetime; + i->second.seqnum = seqnum; +} +void +HwmpRtable::AddProactivePath (uint32_t metric, Mac48Address root, Mac48Address retransmitter, + uint32_t interface, Time lifetime, uint32_t seqnum) +{ + m_root.root = root; + m_root.retransmitter = retransmitter; + m_root.metric = metric; + m_root.whenExpire = Simulator::Now () + lifetime; + m_root.seqnum = seqnum; + m_root.interface = interface; +} +void +HwmpRtable::AddPrecursor (Mac48Address destination, uint32_t precursorInterface, + Mac48Address precursorAddress) +{ + std::pair precursor; + precursor.first = precursorInterface; + precursor.second = precursorAddress; + std::map::iterator i = m_routes.find (destination); + if (i != m_routes.end ()) + { + bool should_add = true; + for (unsigned int j = 0; j < i->second.precursors.size (); j++) + { + //NB: Only one active route may exist, so do not check + //interface ID, just address + if (i->second.precursors[j].second == precursorAddress) + { + should_add = false; + break; + } + } + if (should_add) + { + i->second.precursors.push_back (precursor); + } + } + if (m_root.root == destination) + { + for (unsigned int j = 0; j < m_root.precursors.size (); j++) + { + if (m_root.precursors[j].second == precursorAddress) + { + return; + } + } + } + m_root.precursors.push_back (precursor); +} +void +HwmpRtable::DeleteProactivePath () +{ + m_root.precursors.clear (); + m_root.interface = INTERFACE_ANY; + m_root.metric = MAX_METRIC; + m_root.retransmitter = Mac48Address::GetBroadcast (); + m_root.seqnum = 0; + m_root.whenExpire = Simulator::Now (); +} +void +HwmpRtable::DeleteProactivePath (Mac48Address root) +{ + if (m_root.root == root) + { + DeleteProactivePath (); + } +} +void +HwmpRtable::DeleteReactivePath (Mac48Address destination) +{ + std::map::iterator i = m_routes.find (destination); + if (i != m_routes.end ()) + { + m_routes.erase (i); + } +} +HwmpRtable::LookupResult +HwmpRtable::LookupReactive (Mac48Address destination) +{ + std::map::iterator i = m_routes.find (destination); + if (i == m_routes.end ()) + { + return LookupResult (); + } + if ((i->second.whenExpire < Simulator::Now ()) && (i->second.whenExpire != Seconds (0))) + { + NS_LOG_DEBUG ("Reactive route has expired, sorry."); + return LookupResult (); + } + return LookupReactiveExpired (destination); +} +HwmpRtable::LookupResult +HwmpRtable::LookupReactiveExpired (Mac48Address destination) +{ + std::map::iterator i = m_routes.find (destination); + if (i == m_routes.end ()) + { + return LookupResult (); + } + return LookupResult (i->second.retransmitter, i->second.interface, i->second.metric, i->second.seqnum, + i->second.whenExpire - Simulator::Now ()); +} +HwmpRtable::LookupResult +HwmpRtable::LookupProactive () +{ + if (m_root.whenExpire < Simulator::Now ()) + { + NS_LOG_DEBUG ("Proactive route has expired and will be deleted, sorry."); + DeleteProactivePath (); + } + return LookupProactiveExpired (); +} +HwmpRtable::LookupResult +HwmpRtable::LookupProactiveExpired () +{ + return LookupResult (m_root.retransmitter, m_root.interface, m_root.metric, m_root.seqnum, + m_root.whenExpire - Simulator::Now ()); +} +std::vector +HwmpRtable::GetUnreachableDestinations (Mac48Address peerAddress) +{ + HwmpProtocol::FailedDestination dst; + std::vector retval; + for (std::map::iterator i = m_routes.begin (); i != m_routes.end (); i++) + { + if (i->second.retransmitter == peerAddress) + { + dst.destination = i->first; + i->second.seqnum++; + dst.seqnum = i->second.seqnum; + retval.push_back (dst); + } + } + //Lookup a path to root + if (m_root.retransmitter == peerAddress) + { + dst.destination = m_root.root; + dst.seqnum = m_root.seqnum; + retval.push_back (dst); + } + return retval; +} +HwmpRtable::PrecursorList +HwmpRtable::GetPrecursors (Mac48Address destination) +{ + //We suppose that no duplicates here can be + PrecursorList retval; + std::map::iterator route = m_routes.find (destination); + if (route != m_routes.end ()) + { + for (unsigned int i = 0; i < route->second.precursors.size (); i++) + { + retval.push_back (route->second.precursors[i]); + } + } + if (m_root.root == destination) + { + for (unsigned int i = 0; i < m_root.precursors.size (); i++) + { + bool should_add = true; + for (unsigned int j = 0; j < retval.size (); j++) + { + if (retval[j].second == m_root.precursors[i].second) + { + should_add = false; + break; + } + } + if (should_add) + { + retval.push_back (m_root.precursors[i]); + } + } + } + return retval; +} +bool +HwmpRtable::LookupResult::operator== (const HwmpRtable::LookupResult & o) const +{ + return (retransmitter == o.retransmitter && ifIndex == o.ifIndex && metric == o.metric && seqnum + == o.seqnum); +} +HwmpRtable::LookupResult::LookupResult (Mac48Address r, uint32_t i, uint32_t m, uint32_t s, Time l) : + retransmitter (r), ifIndex (i), metric (m), seqnum (s), lifetime (l) +{ +} +bool +HwmpRtable::LookupResult::IsValid () const +{ + return !(retransmitter == Mac48Address::GetBroadcast () && ifIndex == INTERFACE_ANY && metric == MAX_METRIC + && seqnum == 0); +} +#ifdef RUN_SELF_TESTS +/// Unit test for HwmpRtable +class HwmpRtableTest : public Test +{ +public: + HwmpRtableTest (); + virtual bool + RunTests (); + +private: + /// Test Add apth and lookup path; + void + TestLookup (); + /** + * \name Test add path and try to lookup after entry has expired + * \{ + */ + void + TestAddPath (); + void + TestExpire (); + ///\} + /** + * \name Test add precursors and find precursor list in rtable + * \{ + */ + void + TestPrecursorAdd (); + void + TestPrecursorFind (); + ///\} +private: + bool result; + + Mac48Address dst; + Mac48Address hop; + uint32_t iface; + uint32_t metric; + uint32_t seqnum; + Time expire; + Ptr table; + std::vector precursors; +}; +/// Test instance +static HwmpRtableTest g_HwmpRtableTest; + +HwmpRtableTest::HwmpRtableTest () : + Test ("Mesh/802.11s/HwmpRtable"), result (true), dst ("01:00:00:01:00:01"), hop ("01:00:00:01:00:03"), + iface (8010), metric (10), seqnum (1), expire (Seconds (10)) +{ + precursors.push_back (Mac48Address ("00:10:20:30:40:50")); + precursors.push_back (Mac48Address ("00:11:22:33:44:55")); + precursors.push_back (Mac48Address ("00:01:02:03:04:05")); +} +void +HwmpRtableTest::TestLookup () +{ + HwmpRtable::LookupResult correct (hop, iface, metric, seqnum); + + // Reactive path + table->AddReactivePath (dst, hop, iface, metric, expire, seqnum); + NS_TEST_ASSERT (table->LookupReactive (dst) == correct); + table->DeleteReactivePath (dst); + NS_TEST_ASSERT (!table->LookupReactive (dst).IsValid ()); + + // Proactive + table->AddProactivePath (metric, dst, hop, iface, expire, seqnum); + NS_TEST_ASSERT (table->LookupProactive () == correct); + table->DeleteProactivePath (dst); + NS_TEST_ASSERT (!table->LookupProactive ().IsValid ()); +} +void +HwmpRtableTest::TestAddPath () +{ + table->AddReactivePath (dst, hop, iface, metric, expire, seqnum); + table->AddProactivePath (metric, dst, hop, iface, expire, seqnum); +} +void +HwmpRtableTest::TestExpire () +{ + // this is assumed to be called when path records are already expired + HwmpRtable::LookupResult correct (hop, iface, metric, seqnum); + NS_TEST_ASSERT (table->LookupReactiveExpired (dst) == correct); + NS_TEST_ASSERT (table->LookupProactiveExpired () == correct); + + NS_TEST_ASSERT (!table->LookupReactive (dst).IsValid ()); + NS_TEST_ASSERT (!table->LookupProactive ().IsValid ()); +} +void +HwmpRtableTest::TestPrecursorAdd () +{ + for (std::vector::const_iterator i = precursors.begin (); i != precursors.end (); i++) + { + table->AddPrecursor (dst, iface, *i); + // Check that duplicates are filtered + table->AddPrecursor (dst, iface, *i); + } +} +void +HwmpRtableTest::TestPrecursorFind () +{ + HwmpRtable::PrecursorList precursorList = table->GetPrecursors (dst); + NS_TEST_ASSERT (precursors.size () == precursorList.size ()); + for (unsigned int i = 0; i < precursors.size (); i++) + { + NS_TEST_ASSERT (precursorList[i].first == iface); + NS_TEST_ASSERT (precursorList[i].second == precursors[i]); + } +} +bool +HwmpRtableTest::RunTests () +{ + table = CreateObject (); + + Simulator::Schedule (Seconds (0), &HwmpRtableTest::TestLookup, this); + Simulator::Schedule (Seconds (1), &HwmpRtableTest::TestAddPath, this); + Simulator::Schedule (Seconds (2), &HwmpRtableTest::TestPrecursorAdd, this); + Simulator::Schedule (expire + Seconds (2), &HwmpRtableTest::TestExpire, this); + Simulator::Schedule (expire + Seconds (3), &HwmpRtableTest::TestPrecursorFind, this); + + Simulator::Run (); + Simulator::Destroy (); + + return result; +} +#endif // RUN_SELF_TESTS +} //namespace dot11s +} //namespace ns3 diff --git a/src/devices/mesh/dot11s/hwmp-rtable.h b/src/devices/mesh/dot11s/hwmp-rtable.h new file mode 100644 index 000000000..a22e980f5 --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-rtable.h @@ -0,0 +1,140 @@ +/* -*- 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 + */ + +#ifndef HWMP_RTABLE_H +#define HWMP_RTABLE_H + +#include +#include "ns3/nstime.h" +#include "ns3/mac48-address.h" +#include "ns3/hwmp-protocol.h" +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * + * \brief Routing table for HWMP -- 802.11s routing protocol + */ +class HwmpRtable : public Object +{ +public: + /// Means all interfaces + const static uint32_t INTERFACE_ANY = 0xffffffff; + /// Maximum (the best?) path metric + const static uint32_t MAX_METRIC = 0xffffffff; + + /// Route lookup result, return type of LookupXXX methods + struct LookupResult + { + Mac48Address retransmitter; + uint32_t ifIndex; + uint32_t metric; + uint32_t seqnum; + Time lifetime; + LookupResult (Mac48Address r = Mac48Address::GetBroadcast (), + uint32_t i = INTERFACE_ANY, + uint32_t m = MAX_METRIC, + uint32_t s = 0, + Time l = Seconds (0.0)); + /// True for valid route + bool IsValid () const; + /// Compare route lookup results, used by tests + bool operator==(const LookupResult & o) const; + }; + /// Path precursor = {MAC, interface ID} + typedef std::vector > PrecursorList; + +public: + static TypeId GetTypeId (); + HwmpRtable (); + ~HwmpRtable (); + void DoDispose (); + + ///\name Add/delete paths + //\{ + void AddReactivePath ( + Mac48Address destination, + Mac48Address retransmitter, + uint32_t interface, + uint32_t metric, + Time lifetime, + uint32_t seqnum + ); + void AddProactivePath ( + uint32_t metric, + Mac48Address root, + Mac48Address retransmitter, + uint32_t interface, + Time lifetime, + uint32_t seqnum + ); + void AddPrecursor (Mac48Address destination, uint32_t precursorInterface, Mac48Address precursorAddress); + PrecursorList GetPrecursors (Mac48Address destination); + void DeleteProactivePath (); + void DeleteProactivePath (Mac48Address root); + void DeleteReactivePath (Mac48Address destination); + //\} + + ///\name Lookup + //\{ + /// Lookup path to destination + LookupResult LookupReactive (Mac48Address destination); + /// Return all reactive paths, including expired + LookupResult LookupReactiveExpired (Mac48Address destination); + /// Find proactive path to tree root. Note that calling this method has side effect of deleting expired proactive path + LookupResult LookupProactive (); + /// Return all proactive paths, including expired + LookupResult LookupProactiveExpired (); + //\} + + /// When peer link with a given MAC-address fails - it returns list of unreachable destination addresses + std::vector GetUnreachableDestinations (Mac48Address peerAddress); + +private: + /// Route found in reactive mode + struct ReactiveRoute + { + Mac48Address retransmitter; + uint32_t interface; + uint32_t metric; + Time whenExpire; + uint32_t seqnum; + std::vector > precursors; + }; + /// Route fond in proactive mode + struct ProactiveRoute + { + Mac48Address root; + Mac48Address retransmitter; + uint32_t interface; + uint32_t metric; + Time whenExpire; + uint32_t seqnum; + std::vector > precursors; + }; + + /// List of routes + std::map m_routes; + /// Path to proactive tree root MP + ProactiveRoute m_root; +}; +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/hwmp-tag.cc b/src/devices/mesh/dot11s/hwmp-tag.cc new file mode 100644 index 000000000..f37d90875 --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-tag.cc @@ -0,0 +1,151 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#include "hwmp-tag.h" + +namespace ns3 { +namespace dot11s { + +NS_OBJECT_ENSURE_REGISTERED (HwmpTag); +//Class HwmpTag: +HwmpTag::HwmpTag () : + m_address (Mac48Address::GetBroadcast ()), m_ttl (0), m_metric (0), m_seqno (0) +{ +} + +HwmpTag::~HwmpTag () +{ +} + +void +HwmpTag::SetAddress (Mac48Address retransmitter) +{ + m_address = retransmitter; +} + +Mac48Address +HwmpTag::GetAddress () +{ + return m_address; +} + +void +HwmpTag::SetTtl (uint8_t ttl) +{ + m_ttl = ttl; +} + +uint8_t +HwmpTag::GetTtl () +{ + return m_ttl; +} + +void +HwmpTag::SetMetric (uint32_t metric) +{ + m_metric = metric; +} + +uint32_t +HwmpTag::GetMetric () +{ + return m_metric; +} + +void +HwmpTag::SetSeqno (uint32_t seqno) +{ + m_seqno = seqno; +} + +uint32_t +HwmpTag::GetSeqno () +{ + return m_seqno; +} + +TypeId +HwmpTag::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::HwmpTag") .SetParent () .AddConstructor (); + return tid; +} + +TypeId +HwmpTag::GetInstanceTypeId () const +{ + return GetTypeId (); +} + +uint32_t +HwmpTag::GetSerializedSize () const +{ + return 6 //address + + 1 //ttl + + 4 //metric + + 4; //seqno +} + +void +HwmpTag::Serialize (TagBuffer i) const +{ + uint8_t address[6]; + int j; + m_address.CopyTo (address); + i.WriteU8 (m_ttl); + i.WriteU32 (m_metric); + i.WriteU32 (m_seqno); + for (j = 0; j < 6; j++) + { + i.WriteU8 (address[j]); + } +} + +void +HwmpTag::Deserialize (TagBuffer i) +{ + uint8_t address[6]; + int j; + m_ttl = i.ReadU8 (); + m_metric = i.ReadU32 (); + m_seqno = i.ReadU32 (); + for (j = 0; j < 6; j++) + { + address[j] = i.ReadU8 (); + } + m_address.CopyFrom (address); +} + +void +HwmpTag::Print (std::ostream &os) const +{ + os << "address=" << m_address; + os << "ttl=" << m_ttl; + os << "metrc=" << m_metric; + os << "seqno=" << m_seqno; +} +void +HwmpTag::DecrementTtl () +{ + m_ttl--; +} +} //namespace dot11s +} //namespace ns3 diff --git a/src/devices/mesh/dot11s/hwmp-tag.h b/src/devices/mesh/dot11s/hwmp-tag.h new file mode 100644 index 000000000..c638cc8cc --- /dev/null +++ b/src/devices/mesh/dot11s/hwmp-tag.h @@ -0,0 +1,77 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + * Pavel Boyko + */ + +#ifndef HWMP_TAG_H +#define HWMP_TAG_H + +#include "ns3/tag.h" +#include "ns3/object.h" +#include "ns3/mac48-address.h" +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * + * \brief Hwmp tag implements interaction between HWMP + * protocol and MeshWifiMac + * + * \details Hwmp tag keeps the following: + * 1. When packet is passed from Hwmp to 11sMAC: + * - retransmitter address, + * - TTL value, + * 2. When packet is passed to Hwmp from 11sMAC: + * - lasthop address, + * - TTL value, + * - metric value (metric of link is recalculated + * at each packet, but routing table stores metric + * obtained during path discovery procedure) + */ +class HwmpTag : public Tag +{ +public: + HwmpTag (); + ~HwmpTag (); + void SetAddress (Mac48Address retransmitter); + Mac48Address GetAddress (); + void SetTtl (uint8_t ttl); + uint8_t GetTtl (); + void SetMetric (uint32_t metric); + uint32_t GetMetric (); + void SetSeqno (uint32_t seqno); + uint32_t GetSeqno (); + void DecrementTtl (); + + static TypeId GetTypeId (); + virtual TypeId GetInstanceTypeId () const; + virtual uint32_t GetSerializedSize () const; + virtual void Serialize (TagBuffer i) const; + virtual void Deserialize (TagBuffer i); + virtual void Print (std::ostream &os) const; +private: + Mac48Address m_address; + uint8_t m_ttl; + uint32_t m_metric; + uint32_t m_seqno; +}; +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc new file mode 100644 index 000000000..a02053ab7 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.cc @@ -0,0 +1,233 @@ +/* -*- 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 + */ + +#include "ie-dot11s-beacon-timing.h" +#include "ns3/packet.h" +namespace ns3 { +namespace dot11s { +/******************************************* + * IeBeaconTimingUnit + *******************************************/ +IeBeaconTimingUnit::IeBeaconTimingUnit () : + m_aid (0), m_lastBeacon (0), m_beaconInterval (0) +{ +} +void +IeBeaconTimingUnit::SetAid (uint8_t aid) +{ + m_aid = aid; +} +void +IeBeaconTimingUnit::SetLastBeacon (uint16_t lastBeacon) +{ + m_lastBeacon = lastBeacon; +} +void +IeBeaconTimingUnit::SetBeaconInterval (uint16_t beaconInterval) +{ + m_beaconInterval = beaconInterval; +} +uint8_t +IeBeaconTimingUnit::GetAid () const +{ + return m_aid; +} +uint16_t +IeBeaconTimingUnit::GetLastBeacon () const +{ + return m_lastBeacon; +} +uint16_t +IeBeaconTimingUnit::GetBeaconInterval () const +{ + return m_beaconInterval; +} +/******************************************* + * IeBeaconTiming + *******************************************/ +WifiElementId +IeBeaconTiming::ElementId () const +{ + return IE11S_BEACON_TIMING; +} +IeBeaconTiming::IeBeaconTiming () : + m_numOfUnits (0) +{ +} +IeBeaconTiming::NeighboursTimingUnitsList +IeBeaconTiming::GetNeighboursTimingElementsList () +{ + return m_neighbours; +} +void +IeBeaconTiming::AddNeighboursTimingElementUnit (uint16_t aid, Time last_beacon, //MicroSeconds! + Time beacon_interval //MicroSeconds! +) +{ + if (m_numOfUnits == 50) + { + return; + } + //First we lookup if this element already exists + for (NeighboursTimingUnitsList::const_iterator i = m_neighbours.begin (); i != m_neighbours.end (); i++) + { + if (((*i)->GetAid () == AidToU8 (aid)) && ((*i)->GetLastBeacon () == TimestampToU16 (last_beacon)) + && ((*i)->GetBeaconInterval () == BeaconIntervalToU16 (beacon_interval))) + { + return; + } + } + Ptr new_element = Create (); + new_element->SetAid (AidToU8 (aid)); + new_element->SetLastBeacon (TimestampToU16 (last_beacon)); + new_element->SetBeaconInterval (BeaconIntervalToU16 (beacon_interval)); + m_neighbours.push_back (new_element); + m_numOfUnits++; +} +void +IeBeaconTiming::DelNeighboursTimingElementUnit (uint16_t aid, Time last_beacon, Time beacon_interval) +{ + for (NeighboursTimingUnitsList::iterator i = m_neighbours.begin (); i != m_neighbours.end (); i++) + { + if (((*i)->GetAid () == AidToU8 (aid)) && ((*i)->GetLastBeacon () == TimestampToU16 (last_beacon)) + && ((*i)->GetBeaconInterval () == BeaconIntervalToU16 (beacon_interval))) + { + m_neighbours.erase (i); + m_numOfUnits--; + break; + } + } +} +void +IeBeaconTiming::ClearTimingElement () +{ + uint16_t to_delete = 0; + uint16_t i; + for (NeighboursTimingUnitsList::iterator j = m_neighbours.begin (); j != m_neighbours.end (); j++) + { + to_delete++; + (*j) = 0; + } + for (i = 0; i < to_delete; i++) + { + m_neighbours.pop_back (); + } + m_neighbours.clear (); + +} +uint8_t +IeBeaconTiming::GetInformationSize () const +{ + return (5 * m_numOfUnits ); +} +void +IeBeaconTiming::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "Number of units: " << (uint16_t) m_numOfUnits << std::endl; + for (NeighboursTimingUnitsList::const_iterator j = m_neighbours.begin (); j != m_neighbours.end (); j++) + { + os << "AID=" << (uint16_t) (*j)->GetAid () << ", Last beacon was at " << (*j)->GetLastBeacon () + << ", with beacon interval " << (*j)->GetBeaconInterval () << std::endl; + } + os << "" << std::endl; +} +void +IeBeaconTiming::SerializeInformation (Buffer::Iterator i) const +{ + for (NeighboursTimingUnitsList::const_iterator j = m_neighbours.begin (); j != m_neighbours.end (); j++) + { + i.WriteU8 ((*j)->GetAid ()); + i.WriteHtolsbU16 ((*j)->GetLastBeacon ()); + i.WriteHtolsbU16 ((*j)->GetBeaconInterval ()); + } +} +uint8_t +IeBeaconTiming::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_numOfUnits = length / 5; + for (int j = 0; j < m_numOfUnits; j++) + { + Ptr new_element = Create (); + new_element->SetAid (i.ReadU8 ()); + new_element->SetLastBeacon (i.ReadLsbtohU16 ()); + new_element->SetBeaconInterval (i.ReadLsbtohU16 ()); + m_neighbours.push_back (new_element); + } + return i.GetDistanceFrom (start); +} +; +uint16_t +IeBeaconTiming::TimestampToU16 (Time t) +{ + return ((uint16_t) ((t.GetMicroSeconds () >> 8) & 0xffff)); +} +; + +uint16_t +IeBeaconTiming::BeaconIntervalToU16 (Time t) +{ + return ((uint16_t) (t.GetMicroSeconds () >> 10) & 0xffff); +} +uint8_t +IeBeaconTiming::AidToU8 (uint16_t x) +{ + return (uint8_t) (x & 0xff); +} +bool +operator== (const IeBeaconTimingUnit & a, const IeBeaconTimingUnit & b) +{ + return ((a.GetAid () == b.GetAid ()) && (a.GetLastBeacon () == b.GetLastBeacon ()) + && (a.GetBeaconInterval () == b.GetBeaconInterval ())); +} +bool +IeBeaconTiming::operator== (WifiInformationElement const & a) +{ + try { + IeBeaconTiming const & aa = dynamic_cast(a); + + if (m_numOfUnits != aa.m_numOfUnits) + { + return false; + } + for (unsigned int i = 0; i < m_neighbours.size (); i++) + { + if (!(*PeekPointer (m_neighbours[i]) == *PeekPointer (aa.m_neighbours[i]))) + { + return false; + } + } + return true; + } + catch (std::bad_cast) + { + return false; + } +} +std::ostream & +operator << (std::ostream &os, const IeBeaconTiming &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.h b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.h new file mode 100644 index 000000000..8d4c404e2 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-beacon-timing.h @@ -0,0 +1,115 @@ +/* -*- 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 + */ + +#ifndef WIFI_TIMING_ELEMENT_H +#define WIFI_TIMING_ELEMENT_H + +#include +#include "ns3/nstime.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief Describes one unit of beacon timing element + */ +class IeBeaconTimingUnit : public RefCountBase +{ +public: + IeBeaconTimingUnit (); + void SetAid (uint8_t aid); + void SetLastBeacon (uint16_t lastBeacon); + void SetBeaconInterval (uint16_t beaconInterval); + + uint8_t GetAid () const; + uint16_t GetLastBeacon () const; + uint16_t GetBeaconInterval () const; + +private: + /// Least significant octet of AID: + uint8_t m_aid; + /// Last time we received a beacon in accordance with a local TSF measured in 256 microseconds unit + uint16_t m_lastBeacon; + /// Beacon interval of remote mesh point + uint16_t m_beaconInterval; + friend bool operator== (const IeBeaconTimingUnit & a, const IeBeaconTimingUnit & b); +}; + +/** + * \ingroup dot11s + * \brief See 7.3.2.89 of 802.11s draft 2.07 + */ +class IeBeaconTiming : public WifiInformationElement +{ +public: + /** + * \ingroup dot11s + * This type is a list of timing elements obtained from neigbours with their beacons: + */ + typedef std::vector< Ptr > NeighboursTimingUnitsList; + + IeBeaconTiming (); + /** + * This methods are needed for beacon collision + * avoidance module: + */ + NeighboursTimingUnitsList GetNeighboursTimingElementsList (); + void AddNeighboursTimingElementUnit ( + uint16_t aid, + Time last_beacon, + Time beacon_interval + ); + void DelNeighboursTimingElementUnit ( + uint16_t aid, + Time last_beacon, + Time beacon_interval + ); + void ClearTimingElement (); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual uint8_t GetInformationSize () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator i, uint8_t length); + virtual void Print (std::ostream& os) const; + ///\} + bool operator== (WifiInformationElement const & a); +private: + /** + * Converters: + */ + static uint16_t TimestampToU16 (Time x); + static uint16_t BeaconIntervalToU16 (Time x); + static uint8_t AidToU8 (uint16_t x); + + NeighboursTimingUnitsList m_neighbours; + /** + * Timing element parameters: + */ + uint16_t m_numOfUnits; +}; +bool operator== (const IeBeaconTimingUnit & a, const IeBeaconTimingUnit & b); +std::ostream &operator << (std::ostream &os, const IeBeaconTiming &beaconTiming); +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-configuration.cc b/src/devices/mesh/dot11s/ie-dot11s-configuration.cc new file mode 100644 index 000000000..770930d44 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-configuration.cc @@ -0,0 +1,224 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#include "ie-dot11s-configuration.h" +#include "ns3/packet.h" +namespace ns3 { +namespace dot11s { + +Dot11sMeshCapability::Dot11sMeshCapability () : + acceptPeerLinks (true), MCCASupported (false), MCCAEnabled (false), forwarding (true), beaconTimingReport ( + true), TBTTAdjustment (true), powerSaveLevel (false) +{ +} +uint8_t +Dot11sMeshCapability::GetSerializedSize () const +{ + return 2; +} +uint16_t +Dot11sMeshCapability::GetUint16 () const +{ + uint16_t result = 0; + if (acceptPeerLinks) + { + result |= 1 << 0; + } + if (MCCASupported) + { + result |= 1 << 1; + } + if (MCCAEnabled) + { + result |= 1 << 2; + } + if (forwarding) + { + result |= 1 << 3; + } + if (beaconTimingReport) + { + result |= 1 << 4; + } + if (TBTTAdjustment) + { + result |= 1 << 5; + } + if (powerSaveLevel) + { + result |= 1 << 6; + } + return result; +} +Buffer::Iterator +Dot11sMeshCapability::Serialize (Buffer::Iterator i) const +{ + i.WriteHtolsbU16 (GetUint16 ()); + return i; +} +Buffer::Iterator +Dot11sMeshCapability::Deserialize (Buffer::Iterator i) +{ + uint16_t cap = i.ReadLsbtohU16 (); + acceptPeerLinks = Is (cap, 0); + MCCASupported = Is (cap, 1); + MCCAEnabled = Is (cap, 2); + forwarding = Is (cap, 3); + beaconTimingReport = Is (cap, 4); + TBTTAdjustment = Is (cap, 5); + powerSaveLevel = Is (cap, 6); + return i; +} +bool +Dot11sMeshCapability::Is (uint16_t cap, uint8_t n) const +{ + uint16_t mask = 1 << n; + return (cap & mask); +} +WifiElementId +IeConfiguration::ElementId () const +{ + return IE11S_MESH_CONFIGURATION; +} + +IeConfiguration::IeConfiguration () : + m_APSPId (PROTOCOL_HWMP), m_APSMId (METRIC_AIRTIME), m_CCMId (CONGESTION_NULL), m_SPId ( + SYNC_NEIGHBOUR_OFFSET), m_APId (AUTH_NULL), m_neighbors (0) +{ +} +uint8_t +IeConfiguration::GetInformationSize () const +{ + return 1 // Version + + 4 // APSPId + + 4 // APSMId + + 4 // CCMId + + 4 // SPId + + 4 // APId + + 1 // Mesh formation info (see 7.3.2.86.6 of 802.11s draft 3.0) + + m_meshCap.GetSerializedSize (); +} +void +IeConfiguration::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (1); //Version + // Active Path Selection Protocol ID: + i.WriteHtolsbU32 (m_APSPId); + // Active Path Metric ID: + i.WriteHtolsbU32 (m_APSMId); + // Congestion Control Mode ID: + i.WriteHtolsbU32 (m_CCMId); + // Sync: + i.WriteHtolsbU32 (m_SPId); + // Auth: + i.WriteHtolsbU32 (m_APId); + i.WriteU8 (m_neighbors << 1); + m_meshCap.Serialize (i); +} +uint8_t +IeConfiguration::DeserializeInformation (Buffer::Iterator i, uint8_t length) +{ + Buffer::Iterator start = i; + uint8_t version; + version = i.ReadU8 (); + // Active Path Selection Protocol ID: + m_APSPId = (dot11sPathSelectionProtocol) i.ReadLsbtohU32 (); + // Active Path Metric ID: + m_APSMId = (dot11sPathSelectionMetric) i.ReadLsbtohU32 (); + // Congestion Control Mode ID: + m_CCMId = (dot11sCongestionControlMode) i.ReadLsbtohU32 (); + m_SPId = (dot11sSynchronizationProtocolIdentifier) i.ReadLsbtohU32 (); + m_APId = (dot11sAuthenticationProtocol) i.ReadLsbtohU32 (); + m_neighbors = (i.ReadU8 () >> 1) & 0xF; + i = m_meshCap.Deserialize (i); + return i.GetDistanceFrom (start); +} +void +IeConfiguration::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "Number of neighbors: = " << (uint16_t) m_neighbors + << std::endl << "Active Path Selection Protocol ID: = " << (uint32_t) m_APSPId + << std::endl << "Active Path Selection Metric ID: = " << (uint32_t) m_APSMId + << std::endl << "Congestion Control Mode ID: = " << (uint32_t) m_CCMId + << std::endl << "Synchronize protocol ID: = " << (uint32_t) m_SPId + << std::endl << "Authentication protocol ID: = " << (uint32_t) m_APId + << std::endl << "Capabilities: = " << m_meshCap.GetUint16 () << std::endl; + os << "" << std::endl; +} +void +IeConfiguration::SetRouting (dot11sPathSelectionProtocol routingId) +{ + m_APSPId = routingId; +} +void +IeConfiguration::SetMetric (dot11sPathSelectionMetric metricId) +{ + m_APSMId = metricId; +} +bool +IeConfiguration::IsHWMP () +{ + return (m_APSPId == PROTOCOL_HWMP); +} +bool +IeConfiguration::IsAirtime () +{ + return (m_APSMId == METRIC_AIRTIME); +} +void +IeConfiguration::SetNeighborCount (uint8_t neighbors) +{ + m_neighbors = (neighbors > 31) ? 31 : neighbors; +} +uint8_t +IeConfiguration::GetNeighborCount () +{ + return m_neighbors; +} +Dot11sMeshCapability const& +IeConfiguration::MeshCapability () +{ + return m_meshCap; +} +bool +operator== (const Dot11sMeshCapability & a, const Dot11sMeshCapability & b) +{ + return ((a.acceptPeerLinks == b.acceptPeerLinks) && (a.MCCASupported == b.MCCASupported) && (a.MCCAEnabled + == b.MCCAEnabled) && (a.forwarding == b.forwarding) && (a.beaconTimingReport == b.beaconTimingReport) + && (a.TBTTAdjustment == b.TBTTAdjustment) && (a.powerSaveLevel == b.powerSaveLevel)); +} +bool +operator== (const IeConfiguration & a, const IeConfiguration & b) +{ + return ((a.m_APSPId == b.m_APSPId) && (a.m_APSMId == b.m_APSMId) && (a.m_CCMId == b.m_CCMId) && (a.m_SPId + == b.m_SPId) && (a.m_APId == b.m_APId) && (a.m_neighbors == b.m_neighbors) && (a.m_meshCap + == b.m_meshCap)); +} +std::ostream & +operator << (std::ostream &os, const IeConfiguration &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-configuration.h b/src/devices/mesh/dot11s/ie-dot11s-configuration.h new file mode 100644 index 000000000..f92546539 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-configuration.h @@ -0,0 +1,142 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + + +#ifndef MESH_CONFIGURATION_H +#define MESH_CONFIGURATION_H + +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief See 7.3.2.86.1 in 802.11s draft 3.0 + */ +enum dot11sPathSelectionProtocol +{ + PROTOCOL_HWMP = 0x000fac00, +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.86.2 in 802.11s draft 3.0 + */ +enum dot11sPathSelectionMetric +{ + METRIC_AIRTIME = 0x000fac00, +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.86.3 in 802.11s draft 3.0 + */ +enum dot11sCongestionControlMode +{ + CONGESTION_SIGNALING = 0x000fac00, + CONGESTION_NULL = 0x000facff, +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.86.4 in 802.11s draft 3.0 + */ +enum dot11sSynchronizationProtocolIdentifier +{ + SYNC_NEIGHBOUR_OFFSET = 0x000fac00, + SYNC_NULL = 0x000facff, +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.86.5 in 802.11s draft 3.0 + */ +enum dot11sAuthenticationProtocol +{ + AUTH_NULL = 0x000fac00, + AUTH_SAE = 0x000fac01, +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.86.7 in 802.11s draft 3.0 + */ +class Dot11sMeshCapability +{ +public: + Dot11sMeshCapability (); + uint8_t GetSerializedSize () const; + Buffer::Iterator Serialize (Buffer::Iterator i) const; + Buffer::Iterator Deserialize (Buffer::Iterator i); + uint16_t GetUint16 () const; + bool acceptPeerLinks; + bool MCCASupported; + bool MCCAEnabled; + bool forwarding; + bool beaconTimingReport; + bool TBTTAdjustment; + bool powerSaveLevel; + bool Is (uint16_t cap,uint8_t n) const; + friend bool operator== (const Dot11sMeshCapability & a, const Dot11sMeshCapability & b); +}; + +/** + * \ingroup dot11s + * \brief Describes Mesh Configuration Element + * see 7.3.2.86 of 802.11s draft 3.0 + */ +class IeConfiguration : public WifiInformationElement +{ +public: + IeConfiguration (); + void SetRouting (dot11sPathSelectionProtocol routingId); + void SetMetric (dot11sPathSelectionMetric metricId); + bool IsHWMP (); + bool IsAirtime (); + void SetNeighborCount (uint8_t neighbors); + uint8_t GetNeighborCount (); + Dot11sMeshCapability const& MeshCapability (); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual uint8_t GetInformationSize () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator i, uint8_t length); + virtual void Print (std::ostream& os) const; + ///\} +private: + /** Active Path Selection Protocol ID */ + dot11sPathSelectionProtocol m_APSPId; + /** Active Path Metric ID */ + dot11sPathSelectionMetric m_APSMId; + /** Congestion Control Mode ID */ + dot11sCongestionControlMode m_CCMId; + /** Sync protocol ID */ + dot11sSynchronizationProtocolIdentifier m_SPId; + /** Auth protocol ID */ + dot11sAuthenticationProtocol m_APId; + Dot11sMeshCapability m_meshCap; + uint8_t m_neighbors; + friend bool operator== (const IeConfiguration & a, const IeConfiguration & b); +}; +bool operator== (const IeConfiguration & a, const IeConfiguration & b); +bool operator== (const Dot11sMeshCapability & a, const Dot11sMeshCapability & b); +std::ostream &operator << (std::ostream &os, const IeConfiguration &config); +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-id.cc b/src/devices/mesh/dot11s/ie-dot11s-id.cc new file mode 100644 index 000000000..1af950851 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-id.cc @@ -0,0 +1,145 @@ +/* -*- 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 + */ + +#include "ie-dot11s-id.h" +#include "ns3/assert.h" + +namespace ns3 { +namespace dot11s { +IeMeshId::IeMeshId () +{ + for (uint8_t i = 0; i < 32; i++) + { + m_meshId[i] = 0; + } +} +IeMeshId::IeMeshId (std::string s) +{ + NS_ASSERT (s.size () < 32); + const char *meshid = s.c_str (); + uint8_t len = 0; + while (*meshid != 0 && len < 32) + { + m_meshId[len] = *meshid; + meshid++; + len++; + } + NS_ASSERT (len <= 32); + while (len < 33) + { + m_meshId[len] = 0; + len++; + } +} +WifiElementId +IeMeshId::ElementId () const +{ + return IE11S_MESH_ID; +} +bool +IeMeshId::IsEqual (IeMeshId const &o) const +{ + uint8_t i = 0; + while (i < 32 && m_meshId[i] == o.m_meshId[i] && m_meshId[i] != 0) + { + i++; + } + if (m_meshId[i] != o.m_meshId[i]) + { + return false; + } + return true; +} +bool +IeMeshId::IsBroadcast (void) const +{ + if (m_meshId[0] == 0) + { + return true; + } + return false; +} +char * +IeMeshId::PeekString (void) const +{ + return (char *) m_meshId; +} +uint8_t +IeMeshId::GetInformationSize (void) const +{ + uint8_t size = 0; + while (m_meshId[size] != 0 && size < 32) + { + size++; + } + NS_ASSERT (size <= 32); + return size; +} +void +IeMeshId::SerializeInformation (Buffer::Iterator i) const +{ + uint8_t size = 0; + while (m_meshId[size] != 0 && size < 32) + { + i.WriteU8 (m_meshId[size]); + size++; + } +} +uint8_t +IeMeshId::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + NS_ASSERT (length <= 32); + i.Read (m_meshId, length); + m_meshId[length] = 0; + return i.GetDistanceFrom (start); +} +void +IeMeshId::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "meshId = " << PeekString (); + os << "" << std::endl; +} +bool +operator== (const IeMeshId & a, const IeMeshId & b) +{ + bool result (true); + uint8_t size = 0; + + while (size < 32) + { + result = result && (a.m_meshId[size] == b.m_meshId[size]); + if (a.m_meshId[size] == 0) + { + return result; + } + size++; + } + return result; +} +std::ostream & +operator << (std::ostream &os, const IeMeshId &a) +{ + a.Print (os); + return os; +} +} //namespace dot11s +} // namespace ns3 diff --git a/src/devices/mesh/dot11s/ie-dot11s-id.h b/src/devices/mesh/dot11s/ie-dot11s-id.h new file mode 100644 index 000000000..d9a62cae7 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-id.h @@ -0,0 +1,70 @@ +/* -*- 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 + */ + +#ifndef MESH_ID_H +#define MESH_ID_H + +#include +#include "ns3/buffer.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \brief a IEEE 802.11s Mesh ID 7.3.287 of 802.11s draft 3.0 + * + */ +class IeMeshId : public WifiInformationElement +{ +public: + // broadcast meshId + IeMeshId (); + IeMeshId (std::string s); + + bool IsEqual (IeMeshId const &o) const; + bool IsBroadcast (void) const; + uint32_t GetLength (void) const; + char *PeekString (void) const; + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length); + virtual void Print (std::ostream& os) const; + virtual uint8_t GetInformationSize () const; + ///\} +private: + uint8_t m_meshId[33]; + friend bool operator== (const IeMeshId & a, const IeMeshId & b); +}; + +std::ostream &operator << (std::ostream &os, const IeMeshId &meshId); + +/** + * \class ns3::IeMeshIdValue + * \brief hold objects of type ns3::IeMeshId + */ + +ATTRIBUTE_HELPER_HEADER (IeMeshId); +} //namespace dot11s +} // namespace ns3 +#endif /* MESH_ID_H */ diff --git a/src/devices/mesh/dot11s/ie-dot11s-metric-report.cc b/src/devices/mesh/dot11s/ie-dot11s-metric-report.cc new file mode 100644 index 000000000..c24740a87 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-metric-report.cc @@ -0,0 +1,85 @@ +/* -*- 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 + */ + +#include "ie-dot11s-metric-report.h" +#include "ns3/assert.h" + +namespace ns3 { +namespace dot11s { +IeLinkMetricReport::IeLinkMetricReport () : + m_metric (0) +{ +} +IeLinkMetricReport::IeLinkMetricReport (uint32_t metric) +{ + m_metric = metric; +} +WifiElementId +IeLinkMetricReport::ElementId () const +{ + return IE11S_LINK_METRIC_REPORT; +} +uint8_t +IeLinkMetricReport::GetInformationSize (void) const +{ + return sizeof (uint32_t); +} +uint32_t +IeLinkMetricReport::GetMetric () +{ + return m_metric; +} +void +IeLinkMetricReport::SetMetric (uint32_t metric) +{ + m_metric = metric; +} +void +IeLinkMetricReport::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteHtolsbU32 (m_metric); +} +uint8_t +IeLinkMetricReport::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_metric = i.ReadLsbtohU32 (); + return i.GetDistanceFrom (start); +} +void +IeLinkMetricReport::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "Metric = " << m_metric; + os << "" << std::endl; +} +bool +operator== (const IeLinkMetricReport & a, const IeLinkMetricReport & b) +{ + return (a.m_metric == b.m_metric); +} +std::ostream & +operator << (std::ostream &os, const IeLinkMetricReport &a) +{ + a.Print (os); + return os; +} +} //namespace dot11s +} // namespace ns3 diff --git a/src/devices/mesh/dot11s/ie-dot11s-metric-report.h b/src/devices/mesh/dot11s/ie-dot11s-metric-report.h new file mode 100644 index 000000000..e5521637b --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-metric-report.h @@ -0,0 +1,60 @@ +/* -*- 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 + */ + +#ifndef METRIC_REPORT_H +#define METRIC_REPORT_H + +#include +#include "ns3/buffer.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \brief a IEEE 802.11s Mesh ID 7.3.2.88 of 802.11s draft 3.0 + * + */ +class IeLinkMetricReport : public WifiInformationElement +{ +public: + IeLinkMetricReport (); + IeLinkMetricReport (uint32_t metric); + void SetMetric (uint32_t metric); + uint32_t GetMetric (); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length); + virtual void Print (std::ostream& os) const; + virtual uint8_t GetInformationSize () const; + ///\} +private: + uint32_t m_metric; + friend bool operator== (const IeLinkMetricReport & a, const IeLinkMetricReport & b); + friend bool operator> (const IeLinkMetricReport & a, const IeLinkMetricReport & b); + friend bool operator< (const IeLinkMetricReport & a, const IeLinkMetricReport & b); +}; +std::ostream &operator << (std::ostream &os, const IeLinkMetricReport &linkMetricReport); +} //namespace dot11s +} // namespace ns3 +#endif /* METRIC_REPORT_H */ diff --git a/src/devices/mesh/dot11s/ie-dot11s-peer-management.cc b/src/devices/mesh/dot11s/ie-dot11s-peer-management.cc new file mode 100644 index 000000000..1d507d67e --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-peer-management.cc @@ -0,0 +1,176 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#include "ie-dot11s-peer-management.h" +#include "ns3/assert.h" +#include "ns3/packet.h" + +namespace ns3 { +namespace dot11s { + +IePeerManagement::IePeerManagement () : + m_length (3), m_subtype (PEER_OPEN), m_localLinkId (0), m_peerLinkId (0), m_reasonCode (REASON11S_RESERVED) +{ +} +WifiElementId +IePeerManagement::ElementId () const +{ + return IE11S_PEERING_MANAGEMENT; +} +void +IePeerManagement::SetPeerOpen (uint16_t localLinkId) +{ + m_length = 3; + m_subtype = PEER_OPEN; + m_localLinkId = localLinkId; +} +void +IePeerManagement::SetPeerClose (uint16_t localLinkId, uint16_t peerLinkId, PmpReasonCode reasonCode) +{ + m_length = 7; + m_subtype = PEER_CLOSE; + m_localLinkId = localLinkId; + m_peerLinkId = peerLinkId; + m_reasonCode = reasonCode; +} + +void +IePeerManagement::SetPeerConfirm (uint16_t localLinkId, uint16_t peerLinkId) +{ + m_length = 5; + m_subtype = PEER_CONFIRM; + m_localLinkId = localLinkId; + m_peerLinkId = peerLinkId; +} + +PmpReasonCode +IePeerManagement::GetReasonCode () const +{ + return m_reasonCode; +} + +uint16_t +IePeerManagement::GetLocalLinkId () const +{ + return m_localLinkId; +} + +uint16_t +IePeerManagement::GetPeerLinkId () const +{ + return m_peerLinkId; +} + +uint8_t +IePeerManagement::GetInformationSize (void) const +{ + return m_length; +} +uint8_t +IePeerManagement::GetSubtype () const +{ + return m_subtype; +} +bool +IePeerManagement::SubtypeIsOpen () const +{ + return (m_subtype == PEER_OPEN); +} +bool +IePeerManagement::SubtypeIsClose () const +{ + return (m_subtype == PEER_CLOSE); +} +bool +IePeerManagement::SubtypeIsConfirm () const +{ + return (m_subtype == PEER_CONFIRM); +} + +void +IePeerManagement::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (m_subtype); + i.WriteHtolsbU16 (m_localLinkId); + if (m_length > 3) + { + i.WriteHtolsbU16 (m_peerLinkId); + } + if (m_length > 5) + { + i.WriteHtolsbU16 (m_reasonCode); + } +} +uint8_t +IePeerManagement::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_subtype = i.ReadU8 (); + m_length = length; + if (m_subtype == PEER_OPEN) + { + NS_ASSERT (length == 3); + } + if (m_subtype == PEER_CONFIRM) + { + NS_ASSERT (length == 5); + } + if (m_subtype == PEER_CLOSE) + { + NS_ASSERT (length == 7); + } + m_localLinkId = i.ReadLsbtohU16 (); + if (m_length > 3) + { + m_peerLinkId = i.ReadLsbtohU16 (); + } + if (m_length > 5) + { + m_reasonCode = (PmpReasonCode) i.ReadLsbtohU16 (); + } + return i.GetDistanceFrom (start); +} +void +IePeerManagement::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << " Subtype: = " << (uint16_t) m_subtype << std::endl; + os << " Length: = " << (uint16_t) m_length << std::endl; + os << " LocalLinkId: = " << m_localLinkId << std::endl; + os << " PeerLinkId: = " << m_peerLinkId << std::endl; + os << " ReasonCode: = " << m_reasonCode << std::endl; + os << "" << std::endl; +} +bool +operator== (const IePeerManagement & a, const IePeerManagement & b) +{ + return ((a.m_length == b.m_length) && (a.m_subtype == b.m_subtype) && (a.m_localLinkId == b.m_localLinkId) + && (a.m_peerLinkId == b.m_peerLinkId) && (a.m_reasonCode == b.m_reasonCode)); +} +std::ostream & +operator << (std::ostream &os, const IePeerManagement &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-peer-management.h b/src/devices/mesh/dot11s/ie-dot11s-peer-management.h new file mode 100644 index 000000000..1428fa06d --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-peer-management.h @@ -0,0 +1,101 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#ifndef MESH_PEER_MAN_ELEMENT +#define MESH_PEER_MAN_ELEMENT + +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { + +/** + * \ingroup dot11s + * \brief Codes used by 802.11s Peer Management Protocol + */ +enum PmpReasonCode +{ + REASON11S_PEERING_CANCELLED = 2, // according to open80211s + REASON11S_MESH_MAX_PEERS, + REASON11S_MESH_CAPABILITY_POLICY_VIOLATION, + REASON11S_MESH_CLOSE_RCVD, + REASON11S_MESH_MAX_RETRIES, + REASON11S_MESH_CONFIRM_TIMEOUT, + REASON11S_MESH_INVALID_GTK, + REASON11S_MESH_INCONSISTENT_PARAMETERS, + REASON11S_MESH_INVALID_SECURITY_CAPABILITY, + REASON11S_RESERVED, +}; + +/** + * \ingroup dot11s + * \brief See 7.3.2.85 of draft 2.07 + */ +class IePeerManagement : public WifiInformationElement +{ +public: + IePeerManagement (); + enum Subtype + { + PEER_OPEN = 0, + PEER_CONFIRM, + PEER_CLOSE, + }; + void SetPeerOpen (uint16_t localLinkId); + void SetPeerClose (uint16_t localLinkID, uint16_t peerLinkId, PmpReasonCode reasonCode); + void SetPeerConfirm (uint16_t localLinkID, uint16_t peerLinkId); + + PmpReasonCode GetReasonCode () const; + uint16_t GetLocalLinkId () const; + uint16_t GetPeerLinkId () const; + bool SubtypeIsOpen () const; + bool SubtypeIsClose () const; + bool SubtypeIsConfirm () const ; + uint8_t GetSubtype () const; + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual uint8_t GetInformationSize (void) const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator i, uint8_t length); + virtual void Print (std::ostream& os) const; + ///\} +private: + uint8_t m_length; + uint8_t m_subtype; + uint16_t m_localLinkId; + /** + * Present within confirm and may be present in close + */ + uint16_t m_peerLinkId; + /** + * Present only within close frame + */ + PmpReasonCode m_reasonCode; + friend bool operator== (const IePeerManagement & a, const IePeerManagement & b); +}; +bool operator== (const IePeerManagement & a, const IePeerManagement & b); +std::ostream &operator << (std::ostream &os, const IePeerManagement &peerMan); +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.cc b/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.cc new file mode 100644 index 000000000..9cf0f5f86 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.cc @@ -0,0 +1,65 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#include "ie-dot11s-peering-protocol.h" +namespace ns3 { +namespace dot11s { +uint8_t +IePeeringProtocol::GetInformationSize () const +{ + return 1; +} +IePeeringProtocol::IePeeringProtocol () : + m_protocol (0) +{ +} +WifiElementId +IePeeringProtocol::ElementId () const +{ + return IE11S_MESH_PEERING_PROTOCOL_VERSION; +} +void +IePeeringProtocol::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (m_protocol); +} +uint8_t +IePeeringProtocol::DeserializeInformation (Buffer::Iterator i, uint8_t length) +{ + Buffer::Iterator start = i; + m_protocol = i.ReadU8 (); + return i.GetDistanceFrom (start); +} +void +IePeeringProtocol::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "peering protocol = " << m_protocol; + os << "" << std::endl; +} +std::ostream & +operator << (std::ostream &os, const IePeeringProtocol &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.h b/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.h new file mode 100644 index 000000000..77e09171f --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-peering-protocol.h @@ -0,0 +1,49 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#ifndef MESH_PERING_PROTOCOL_H +#define MESH_PEERING_PROTOCOL_H + +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 +{ +namespace dot11s +{ +class IePeeringProtocol : public WifiInformationElement +{ +public: + IePeeringProtocol (); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual uint8_t GetInformationSize () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator i, uint8_t length); + virtual void Print (std::ostream& os) const; + ///\} +private: + uint8_t m_protocol; +}; +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-perr.cc b/src/devices/mesh/dot11s/ie-dot11s-perr.cc new file mode 100644 index 000000000..b7aed0dd4 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-perr.cc @@ -0,0 +1,165 @@ +/* -*- 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 + */ + +#include "ie-dot11s-perr.h" +#include "ns3/address-utils.h" +#include "ns3/packet.h" +namespace ns3 { +namespace dot11s { +IePerr::IePerr () +{ +} +IePerr::~IePerr () +{ +} +WifiElementId +IePerr::ElementId () const +{ + return IE11S_PERR; +} +void +IePerr::Print (std::ostream &os) const +{ + os << std::endl << "" << std::endl; + os << "Number of failed destinations: = " << m_addressUnits.size (); + for (unsigned int j = 0; j < m_addressUnits.size (); j++) + { + os << "Failed destination address: = " << m_addressUnits[j].destination << ", sequence number = " + << m_addressUnits[j].seqnum; + } + os << std::endl << "" << std::endl; +} +uint8_t +IePerr::GetNumOfDest () const +{ + return m_addressUnits.size (); +} +void +IePerr::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (0); + i.WriteU8 (m_addressUnits.size ()); + for (unsigned int j = 0; j < m_addressUnits.size (); j++) + { + WriteTo (i, m_addressUnits[j].destination); + i.WriteHtolsbU32 (m_addressUnits[j].seqnum); + } +} +uint8_t +IePerr::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + i.Next (1); //Mode flags is not used now + uint8_t numOfDest = i.ReadU8 (); + NS_ASSERT ((2 + 10 * numOfDest ) == length); + length = 0; //to avoid compiler warning in optimized builds + for (unsigned int j = 0; j < numOfDest; j++) + { + HwmpProtocol::FailedDestination unit; + ReadFrom (i, unit.destination); + unit.seqnum = i.ReadLsbtohU32 (); + m_addressUnits.push_back (unit); + } + return i.GetDistanceFrom (start); +} + +uint8_t +IePerr::GetInformationSize () const +{ + uint8_t retval = 1 //ModeFlags + + 1 //NumOfDests + + (6 + 4) * m_addressUnits.size (); + return retval; +} + +void +IePerr::AddAddressUnit (HwmpProtocol::FailedDestination unit) +{ + for (unsigned int i = 0; i < m_addressUnits.size (); i++) + { + if (m_addressUnits[i].destination == unit.destination) + { + return; + } + } + if ((m_addressUnits.size () + 1) * 10 + 2 > 255) + { + return; + } + m_addressUnits.push_back (unit); +} +bool +IePerr::IsFull () const +{ + return (GetInformationSize () + 2 /* ID + LENGTH*/+ 10 /* Size of Mac48Address + uint32_t (one unit)*/> 255); +} +std::vector +IePerr::GetAddressUnitVector () const +{ + return m_addressUnits; +} +void +IePerr::DeleteAddressUnit (Mac48Address address) +{ + for (std::vector::iterator i = m_addressUnits.begin (); i + != m_addressUnits.end (); i++) + { + if (i->destination == address) + { + m_addressUnits.erase (i); + break; + } + } +} +void +IePerr::ResetPerr () +{ + m_addressUnits.clear (); +} +bool +operator== (const IePerr & a, const IePerr & b) +{ + if (a.m_addressUnits.size () != b.m_addressUnits.size ()) + { + return false; + } + for (unsigned int i = 0; i < a.m_addressUnits.size (); i++) + { + if (a.m_addressUnits[i].destination != b.m_addressUnits[i].destination) + { + return false; + } + if (a.m_addressUnits[i].seqnum != b.m_addressUnits[i].seqnum) + { + return false; + } + } + return true; +} +std::ostream & +operator << (std::ostream & os, const IePerr & a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + + diff --git a/src/devices/mesh/dot11s/ie-dot11s-perr.h b/src/devices/mesh/dot11s/ie-dot11s-perr.h new file mode 100644 index 000000000..110a2f5ce --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-perr.h @@ -0,0 +1,63 @@ +/* -*- 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 + */ + +#ifndef PERR_INFORMATION_ELEMENT_H +#define PERR_INFORMATION_ELEMENT_H + +#include "ns3/mac48-address.h" +#include "ns3/wifi-information-element-vector.h" +#include "ns3/hwmp-protocol.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief See 7.3.2.98 of 802.11s draft 2.07 + */ +class IePerr : public WifiInformationElement +{ +public: + IePerr (); + ~IePerr (); + uint8_t GetNumOfDest () const; + void AddAddressUnit (HwmpProtocol::FailedDestination unit); + bool IsFull () const; + std::vector GetAddressUnitVector () const; + void DeleteAddressUnit (Mac48Address address); + void ResetPerr (); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length); + virtual void Print (std::ostream& os) const; + virtual uint8_t GetInformationSize () const; + ///\} +private: + std::vector m_addressUnits; + friend bool operator== (const IePerr & a, const IePerr & b); +}; + bool operator== (const IePerr & a, const IePerr & b); + std::ostream &operator << (std::ostream &os, const IePerr &perr); +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/ie-dot11s-prep.cc b/src/devices/mesh/dot11s/ie-dot11s-prep.cc new file mode 100644 index 000000000..dd58053cf --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-prep.cc @@ -0,0 +1,214 @@ +/* -*- 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 + */ + +#include "ie-dot11s-prep.h" +#include "ns3/address-utils.h" +#include "ns3/assert.h" +#include "ns3/packet.h" +namespace ns3 { +namespace dot11s { +/******************************** + * IePrep + *******************************/ +IePrep::~IePrep () +{ +} +IePrep::IePrep () : + m_flags (0), m_hopcount (0), m_ttl (0), m_destinationAddress (Mac48Address::GetBroadcast ()), + m_destSeqNumber (0), m_lifetime (0), m_metric (0), m_originatorAddress (Mac48Address::GetBroadcast ()), + m_originatorSeqNumber (0) +{ +} +WifiElementId +IePrep::ElementId () const +{ + return IE11S_PREP; +} +void +IePrep::SetFlags (uint8_t flags) +{ + m_flags = flags; +} +void +IePrep::SetHopcount (uint8_t hopcount) +{ + m_hopcount = hopcount; +} +void +IePrep::SetTtl (uint8_t ttl) +{ + m_ttl = ttl; +} +void +IePrep::SetDestinationSeqNumber (uint32_t destSeqNumber) +{ + m_destSeqNumber = destSeqNumber; +} +void +IePrep::SetDestinationAddress (Mac48Address destAddress) +{ + m_destinationAddress = destAddress; +} +void +IePrep::SetMetric (uint32_t metric) +{ + m_metric = metric; +} +void +IePrep::SetOriginatorAddress (Mac48Address originatorAddress) +{ + m_originatorAddress = originatorAddress; +} +void +IePrep::SetOriginatorSeqNumber (uint32_t originatorSeqNumber) +{ + m_originatorSeqNumber = originatorSeqNumber; +} +void +IePrep::SetLifetime (uint32_t lifetime) +{ + m_lifetime = lifetime; +} +uint8_t +IePrep::GetFlags () const +{ + return m_flags; +} +uint8_t +IePrep::GetHopcount () const +{ + return m_hopcount; +} +uint32_t +IePrep::GetTtl () const +{ + return m_ttl; +} +uint32_t +IePrep::GetDestinationSeqNumber () const +{ + return m_destSeqNumber; +} +Mac48Address +IePrep::GetDestinationAddress () const +{ + return m_destinationAddress; +} +uint32_t +IePrep::GetMetric () const +{ + return m_metric; +} +Mac48Address +IePrep::GetOriginatorAddress () const +{ + return m_originatorAddress; +} +uint32_t +IePrep::GetOriginatorSeqNumber () const +{ + return m_originatorSeqNumber; +} +uint32_t +IePrep::GetLifetime () const +{ + return m_lifetime; +} +void +IePrep::DecrementTtl () +{ + m_ttl--; +} + +void +IePrep::IncrementMetric (uint32_t metric) +{ + m_metric += metric; +} + +void +IePrep::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (m_flags); + i.WriteU8 (m_hopcount); + i.WriteU8 (m_ttl); + WriteTo (i, m_destinationAddress); + i.WriteHtolsbU32 (m_destSeqNumber); + i.WriteHtolsbU32 (m_lifetime); + i.WriteHtolsbU32 (m_metric); + WriteTo (i, m_originatorAddress); + i.WriteHtolsbU32 (m_originatorSeqNumber); +} +uint8_t +IePrep::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_flags = i.ReadU8 (); + m_hopcount = i.ReadU8 (); + m_ttl = i.ReadU8 (); + ReadFrom (i, m_destinationAddress); + m_destSeqNumber = i.ReadLsbtohU32 (); + m_lifetime = i.ReadLsbtohU32 (); + m_metric = i.ReadLsbtohU32 (); + ReadFrom (i, m_originatorAddress); + m_originatorSeqNumber = i.ReadLsbtohU32 (); + return i.GetDistanceFrom (start); +} +uint8_t +IePrep::GetInformationSize () const +{ + uint32_t retval = 1 //Flags + + 1 //Hopcount + + 1 //Ttl + + 6 //Dest address + + 4 //Dest seqno + + 4 //Lifetime + + 4 //metric + + 6 //Originator address + + 4; //Originator seqno + return retval; +} +void +IePrep::Print (std::ostream& os) const +{ + os << std::endl << "" << std::endl; + os << "Flags: = " << m_flags << std::endl << "Hopcount: = " << m_hopcount << std::endl << "TTL: = " << m_ttl + << std::endl<< "Destination: = " << m_destinationAddress << std::endl << "Dest. seqnum: = " << m_destSeqNumber + << std::endl << "Lifetime: = " << m_lifetime << std::endl<< "Metric: = " << m_metric << std::endl << "Originator: = " + << m_originatorAddress << std::endl << "Orig. seqnum: = " << m_originatorSeqNumber << std::endl; + os << "" << std::endl; +} +bool +operator== (const IePrep & a, const IePrep & b) +{ + return ((a.m_flags == b.m_flags) && (a.m_hopcount == b.m_hopcount) && (a.m_ttl == b.m_ttl) + && (a.m_destinationAddress == b.m_destinationAddress) && (a.m_destSeqNumber == b.m_destSeqNumber) + && (a.m_lifetime == b.m_lifetime) && (a.m_metric == b.m_metric) && (a.m_originatorAddress + == b.m_originatorAddress) && (a.m_originatorSeqNumber == b.m_originatorSeqNumber)); +} +std::ostream & +operator << (std::ostream &os, const IePrep &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-prep.h b/src/devices/mesh/dot11s/ie-dot11s-prep.h new file mode 100644 index 000000000..a16563be6 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-prep.h @@ -0,0 +1,87 @@ +/* -*- 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 + */ + +#ifndef WIFI_PREP_INFORMATION_ELEMENT_H +#define WIFI_PREP_INFORMATION_ELEMENT_H + +#include "ns3/mac48-address.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief See 7.3.2.97 of 802.11s draft 2.07 + */ +class IePrep : public WifiInformationElement +{ +public: + IePrep (); + ~IePrep (); + void SetFlags (uint8_t flags); + void SetHopcount (uint8_t hopcount); + void SetTtl (uint8_t ttl); + void SetDestinationAddress (Mac48Address dest_address); + void SetDestinationSeqNumber (uint32_t dest_seq_number); + void SetLifetime (uint32_t lifetime); + void SetMetric (uint32_t metric); + void SetOriginatorAddress (Mac48Address originator_address); + void SetOriginatorSeqNumber (uint32_t originator_seq_number); + + uint8_t GetFlags () const; + uint8_t GetHopcount () const; + uint32_t GetTtl () const; + Mac48Address GetDestinationAddress () const; + uint32_t GetDestinationSeqNumber () const; + uint32_t GetLifetime () const; + uint32_t GetMetric () const; + Mac48Address GetOriginatorAddress () const; + uint32_t GetOriginatorSeqNumber ()const ; + + void DecrementTtl (); + void IncrementMetric (uint32_t metric); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length); + virtual uint8_t GetInformationSize () const; + virtual void Print (std::ostream& os) const; + ///\} +private: + uint8_t m_flags; + uint8_t m_hopcount; + uint8_t m_ttl; + Mac48Address m_destinationAddress; + uint32_t m_destSeqNumber; + uint32_t m_lifetime; + uint32_t m_metric; + Mac48Address m_originatorAddress; + uint32_t m_originatorSeqNumber; + friend bool operator== (const IePrep & a, const IePrep & b); +}; +bool operator== (const IePrep & a, const IePrep & b); +std::ostream &operator << (std::ostream &os, const IePrep &prep); +} // namespace dot11s +} //namespace ns3 +#endif + diff --git a/src/devices/mesh/dot11s/ie-dot11s-preq.cc b/src/devices/mesh/dot11s/ie-dot11s-preq.cc new file mode 100644 index 000000000..c5d2c1ff9 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-preq.cc @@ -0,0 +1,450 @@ +/* -*- 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 + */ + +#include "ie-dot11s-preq.h" +#include "ns3/address-utils.h" +#include "ns3/assert.h" +#include "ns3/packet.h" + +namespace ns3 { +namespace dot11s { +/************************* + * DestinationAddressUnit + ************************/ +DestinationAddressUnit::DestinationAddressUnit () : + m_do (false), m_rf (false), m_usn (false), m_destinationAddress (Mac48Address ()), m_destSeqNumber (0) +{ +} +void +DestinationAddressUnit::SetFlags (bool doFlag, bool rfFlag, bool usnFlag) +{ + m_do = doFlag; + m_rf = rfFlag; + m_usn = usnFlag; +} + +void +DestinationAddressUnit::SetDestSeqNumber (uint32_t dest_seq_number) +{ + m_destSeqNumber = dest_seq_number; + if (m_destSeqNumber != 0) + { + m_usn = true; + } +} +void +DestinationAddressUnit::SetDestinationAddress (Mac48Address dest_address) +{ + m_destinationAddress = dest_address; +} +bool +DestinationAddressUnit::IsDo () +{ + return m_do; +} + +bool +DestinationAddressUnit::IsRf () +{ + return m_rf; +} +bool +DestinationAddressUnit::IsUsn () +{ + return m_usn; +} +uint32_t +DestinationAddressUnit::GetDestSeqNumber () const +{ + return m_destSeqNumber; +} +Mac48Address +DestinationAddressUnit::GetDestinationAddress () const +{ + return m_destinationAddress; +} +/******************************** + * IePreq + *******************************/ +IePreq::~IePreq () +{ +} +IePreq::IePreq () : + m_maxSize (32), m_flags (0), m_hopCount (0), m_ttl (0), m_preqId (0), m_originatorAddress ( + Mac48Address::GetBroadcast ()), m_originatorSeqNumber (0), m_lifetime (0), m_metric (0), + m_destCount (0) +{ +} +WifiElementId +IePreq::ElementId () const +{ + return IE11S_PREQ; +} +void +IePreq::SetUnicastPreq () +{ + m_flags |= 1 << 1; +} + +void +IePreq::SetNeedNotPrep () +{ + m_flags |= 1 << 2; +} +void +IePreq::SetHopcount (uint8_t hopcount) +{ + m_hopCount = hopcount; +} +void +IePreq::SetTTL (uint8_t ttl) +{ + m_ttl = ttl; +} +void +IePreq::SetPreqID (uint32_t preq_id) +{ + m_preqId = preq_id; +} +void +IePreq::SetMetric (uint32_t metric) +{ + m_metric = metric; +} +void +IePreq::SetOriginatorAddress (Mac48Address originator_address) +{ + m_originatorAddress = originator_address; +} +void +IePreq::SetOriginatorSeqNumber (uint32_t originator_seq_number) +{ + m_originatorSeqNumber = originator_seq_number; +} +void +IePreq::SetLifetime (uint32_t lifetime) +{ + m_lifetime = lifetime; +} +void +IePreq::SetDestCount (uint8_t dest_count) +{ + m_destCount = dest_count; +} +bool +IePreq::IsUnicastPreq () const +{ + return (m_flags & (1 << 1)); +} +bool +IePreq::IsNeedNotPrep () const +{ + return (m_flags & (1 << 2)); +} +uint8_t +IePreq::GetHopCount () const +{ + return m_hopCount; +} +uint8_t +IePreq::GetTtl () const +{ + return m_ttl; +} +uint32_t +IePreq::GetPreqID () const +{ + return m_preqId; +} +uint32_t +IePreq::GetMetric () const +{ + return m_metric; +} +Mac48Address +IePreq::GetOriginatorAddress () const +{ + return m_originatorAddress; +} +uint32_t +IePreq::GetOriginatorSeqNumber () const +{ + return m_originatorSeqNumber; +} +uint32_t +IePreq::GetLifetime () const +{ + return m_lifetime; +} + +uint8_t +IePreq::GetDestCount () const +{ + return m_destCount; +} +void +IePreq::DecrementTtl () +{ + m_ttl--; + m_hopCount++; +} +void +IePreq::IncrementMetric (uint32_t metric) +{ + m_metric += metric; +} +void +IePreq::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (m_flags); + i.WriteU8 (m_hopCount); + i.WriteU8 (m_ttl); + i.WriteHtolsbU32 (m_preqId); + WriteTo (i, m_originatorAddress); + i.WriteHtolsbU32 (m_originatorSeqNumber); + i.WriteHtolsbU32 (m_lifetime); + i.WriteHtolsbU32 (m_metric); + i.WriteU8 (m_destCount); + int written = 0; + for (std::vector >::const_iterator j = m_destinations.begin (); j + != m_destinations.end (); j++) + { + uint8_t flags = 0; + if ((*j)->IsDo ()) + { + flags |= 1 << 0; + } + if ((*j)->IsRf ()) + { + flags |= 1 << 1; + } + if ((*j)->IsUsn ()) + { + flags |= 1 << 2; + } + i.WriteU8 (flags); + WriteTo (i, (*j)->GetDestinationAddress ()); + i.WriteHtolsbU32 ((*j)->GetDestSeqNumber ()); + written++; + if (written > m_maxSize) + { + break; + } + } +} +uint8_t +IePreq::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_flags = i.ReadU8 (); + m_hopCount = i.ReadU8 (); + m_ttl = i.ReadU8 (); + m_preqId = i.ReadLsbtohU32 (); + ReadFrom (i, m_originatorAddress); + m_originatorSeqNumber = i.ReadLsbtohU32 (); + m_lifetime = i.ReadLsbtohU32 (); + m_metric = i.ReadLsbtohU32 (); + m_destCount = i.ReadU8 (); + for (int j = 0; j < m_destCount; j++) + { + Ptr new_element = Create (); + bool doFlag = false; + bool rfFlag = false; + bool usnFlag = false; + uint8_t flags = i.ReadU8 (); + if (flags & (1 << 0)) + { + doFlag = true; + } + if (flags & (1 << 1)) + { + rfFlag = true; + } + if (flags & (1 << 2)) + { + usnFlag = true; + } + new_element->SetFlags (doFlag, rfFlag, usnFlag); + Mac48Address addr; + ReadFrom (i, addr); + new_element->SetDestinationAddress (addr); + new_element->SetDestSeqNumber (i.ReadLsbtohU32 ()); + m_destinations.push_back (new_element); + NS_ASSERT (28 + j * 11 < length); + } + return i.GetDistanceFrom (start); +} +uint8_t +IePreq::GetInformationSize () const +{ + uint8_t retval = 1 //Flags + + 1 //Hopcount + + 1 //TTL + + 4 //PREQ ID + + 6 //Source address (originator) + + 4 //Originator seqno + + 4 //Lifetime + + 4 //metric + + 1; //destination count + if (m_destCount > m_maxSize) + { + retval += (m_maxSize * 11); + } + else + { + retval += (m_destCount * 11); + } + return retval; +} +void +IePreq::Print (std::ostream &os) const +{ + os << std::endl << "" << std::endl; + os << " originator address = " << m_originatorAddress << "std::endl"; + os << " TTL = " << (uint16_t) m_ttl << "std::endl"; + os << " hop count = " << (uint16_t) m_hopCount << "std::endl"; + os << " metric = " << m_metric << "std::endl"; + os << " seqno = " << m_originatorSeqNumber << "std::endl"; + os << " lifetime = " << m_lifetime << "std::endl"; + os << " preq ID = " << m_preqId << "std::endl"; + os << " Destinations are:std::endl"; + for (int j = 0; j < m_destCount; j++) + { + os << " " << m_destinations[j]->GetDestinationAddress () << "std::endl"; + } + os << "" << std::endl; +} +std::vector > +IePreq::GetDestinationList () +{ + return m_destinations; +} +void +IePreq::AddDestinationAddressElement (bool doFlag, bool rfFlag, Mac48Address dest_address, + uint32_t dest_seq_number) +{ + for (std::vector >::const_iterator i = m_destinations.begin (); i + != m_destinations.end (); i++) + { + if ((*i)->GetDestinationAddress () == dest_address) + { + return; + } + } + //TODO: check overflow + Ptr new_element = Create (); + new_element->SetFlags (doFlag, rfFlag, (dest_seq_number == 0)); + new_element->SetDestinationAddress (dest_address); + new_element->SetDestSeqNumber (dest_seq_number); + m_destinations.push_back (new_element); + m_destCount++; +} +void +IePreq::DelDestinationAddressElement (Mac48Address dest_address) +{ + for (std::vector >::iterator i = m_destinations.begin (); i + != m_destinations.end (); i++) + { + if ((*i)->GetDestinationAddress () == dest_address) + { + m_destinations.erase (i); + m_destCount--; + break; + } + } +} +void +IePreq::ClearDestinationAddressElements () +{ + int i; + for (std::vector >::iterator j = m_destinations.begin (); j + != m_destinations.end (); j++) + { + (*j) = 0; + } + for (i = 0; i < m_destCount; i++) + { + m_destinations.pop_back (); + } + m_destinations.clear (); + m_destCount = 0; +} +bool +operator== (const DestinationAddressUnit & a, const DestinationAddressUnit & b) +{ + return (a.m_do == b.m_do && a.m_rf == b.m_rf && a.m_usn == b.m_usn && a.m_destinationAddress + == b.m_destinationAddress && a.m_destSeqNumber == b.m_destSeqNumber); +} +bool +operator== (const IePreq & a, const IePreq & b) +{ + bool ok = (a.m_flags == b.m_flags && a.m_hopCount == b.m_hopCount && a.m_ttl == b.m_ttl && a.m_preqId + == b.m_preqId && a.m_originatorAddress == b.m_originatorAddress && a.m_originatorSeqNumber + == b.m_originatorSeqNumber && a.m_lifetime == b.m_lifetime && a.m_metric == b.m_metric && a.m_destCount + == b.m_destCount); + + if (!ok) + { + return false; + } + if (a.m_destinations.size () != b.m_destinations.size ()) + { + return false; + } + for (size_t i = 0; i < a.m_destinations.size (); ++i) + { + if (!(*(PeekPointer (a.m_destinations[i])) == *(PeekPointer (b.m_destinations[i])))) + { + return false; + } + } + return true; +} +bool +IePreq::MayAddAddress (Mac48Address originator) +{ + if (m_originatorAddress != originator) + { + return false; + } + if (m_destinations[0]->GetDestinationAddress () == Mac48Address::GetBroadcast ()) + { + return false; + } + if ((GetInformationSize () + 11) > 255) + { + return false; + } + return true; +} +bool +IePreq::IsFull () const +{ + return ((GetInformationSize () + 11) > 255); +} +std::ostream & +operator << (std::ostream &os, const IePreq &a) +{ + a.Print (os); + return os; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/ie-dot11s-preq.h b/src/devices/mesh/dot11s/ie-dot11s-preq.h new file mode 100644 index 000000000..8471f6673 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-preq.h @@ -0,0 +1,160 @@ +/* -*- 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 + */ + +#ifndef WIFI_PREQ_INFORMATION_ELEMENT_H +#define WIFI_PREQ_INFORMATION_ELEMENT_H + +#include + +#include "ns3/mac48-address.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief Describes an address unit in PREQ information element + * See 7.3.2.96 for more details + */ +class DestinationAddressUnit : public RefCountBase +{ +public: + DestinationAddressUnit (); + void SetFlags (bool doFlag, bool rfFlag, bool usnFlag); + void SetDestinationAddress (Mac48Address dest_address); + void SetDestSeqNumber (uint32_t dest_seq_number); + bool IsDo (); + bool IsRf (); + bool IsUsn (); + Mac48Address GetDestinationAddress () const; + uint32_t GetDestSeqNumber () const; +private: + bool m_do; + bool m_rf; + bool m_usn; + Mac48Address m_destinationAddress; + uint32_t m_destSeqNumber; + + friend bool operator== (const DestinationAddressUnit & a, const DestinationAddressUnit & b); +}; +/** + * \ingroup dot11s + * \brief See 7.3.2.96 of 802.11s draft 2.07 + */ +class IePreq : public WifiInformationElement +{ +public: + IePreq (); + ~IePreq (); + /** + * Add a destination address unit: flags, destination and sequence + * number + */ + void AddDestinationAddressElement ( + bool doFlag, + bool rfFlag, + Mac48Address dest_address, + uint32_t dest_seq_number + ); + /// Delete a destination address unit by destination + void DelDestinationAddressElement (Mac48Address dest_address); + /// Clear PREQ: remove all destinations + void ClearDestinationAddressElements (); + /// Get all destinations, which are stored in PREQ: + std::vector > GetDestinationList (); + /// SetProper flags which indicate that PREQ is unicast + void SetUnicastPreq (); + /* + * \brief In proactive case: need we send PREP + */ + void SetNeedNotPrep (); + ///\name Setters for fields: + ///\{ + void SetHopcount (uint8_t hopcount); + void SetTTL (uint8_t ttl); + void SetPreqID (uint32_t id); + void SetOriginatorAddress (Mac48Address originator_address); + void SetOriginatorSeqNumber (uint32_t originator_seq_number); + void SetLifetime (uint32_t lifetime); + void SetMetric (uint32_t metric); + void SetDestCount (uint8_t dest_count); + ///\} + ///\name Getters for fields: + ///\{ + bool IsUnicastPreq () const; + bool IsNeedNotPrep () const; + uint8_t GetHopCount () const; + uint8_t GetTtl ()const ; + uint32_t GetPreqID () const; + Mac48Address GetOriginatorAddress () const; + uint32_t GetOriginatorSeqNumber () const; + uint32_t GetLifetime () const; + uint32_t GetMetric () const; + uint8_t GetDestCount () const; + ///\} + /// Handle TTL and Metric: + void DecrementTtl (); + void IncrementMetric (uint32_t metric); + /* + * \brief Checks that preq's originator address equals to originator, and + * this preq is not proactive + */ + bool MayAddAddress (Mac48Address originator); + bool IsFull () const; + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator i, uint8_t length); + virtual uint8_t GetInformationSize () const; + virtual void Print (std::ostream& os) const; + ///\} +private: + /** + * how many destinations we support + */ + uint8_t m_maxSize; //TODO: make as an attrubute + /** + * Fields of information element: + */ + uint8_t m_flags; + uint8_t m_hopCount; + uint8_t m_ttl; + uint32_t m_preqId; + Mac48Address m_originatorAddress; + uint32_t m_originatorSeqNumber; + uint32_t m_lifetime; + uint32_t m_metric; + uint8_t m_destCount; + std::vector > m_destinations; + + friend bool operator== (const IePreq & a, const IePreq & b); +}; + +bool operator== (const DestinationAddressUnit & a, const DestinationAddressUnit & b); +bool operator== (const IePreq & a, const IePreq & b); +std::ostream &operator << (std::ostream &os, const IePreq &preq); + +} // namespace dot11s +} //namespace ns3 +#endif + diff --git a/src/devices/mesh/dot11s/ie-dot11s-rann.cc b/src/devices/mesh/dot11s/ie-dot11s-rann.cc new file mode 100644 index 000000000..8b5ad2887 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-rann.cc @@ -0,0 +1,180 @@ +/* -*- 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 + */ + +#include "ie-dot11s-rann.h" +#include "ns3/assert.h" +#include "ns3/address-utils.h" +#include "ns3/packet.h" + +namespace ns3 { +namespace dot11s { + +IeRann::~IeRann () +{ +} +IeRann::IeRann () : + m_flags (0), m_hopcount (0), m_ttl (0), m_originatorAddress (Mac48Address::GetBroadcast ()), + m_destSeqNumber (0), m_metric (0) +{ +} +WifiElementId +IeRann::ElementId () const +{ + return IE11S_RANN; +} + +void +IeRann::SetFlags (uint8_t flags) +{ + m_flags = flags; +} +void +IeRann::SetHopcount (uint8_t hopcount) +{ + m_hopcount = hopcount; +} +void +IeRann::SetTTL (uint8_t ttl) +{ + m_ttl = ttl; +} +void +IeRann::SetDestSeqNumber (uint32_t dest_seq_number) +{ + m_destSeqNumber = dest_seq_number; +} +void +IeRann::SetMetric (uint32_t metric) +{ + m_metric = metric; +} +void +IeRann::SetOriginatorAddress (Mac48Address originator_address) +{ + m_originatorAddress = originator_address; +} + +uint8_t +IeRann::GetFlags () +{ + return m_flags; +} +uint8_t +IeRann::GetHopcount () +{ + return m_hopcount; +} +uint8_t +IeRann::GetTtl () +{ + return m_ttl; +} +uint32_t +IeRann::GetDestSeqNumber () +{ + return m_destSeqNumber; +} +uint32_t +IeRann::GetMetric () +{ + return m_metric; +} +void +IeRann::DecrementTtl () +{ + m_ttl--; + m_hopcount++; +} + +void +IeRann::IncrementMetric (uint32_t m) +{ + m_metric += m; +} + +Mac48Address +IeRann::GetOriginatorAddress () +{ + return m_originatorAddress; +} +void +IeRann::SerializeInformation (Buffer::Iterator i) const +{ + i.WriteU8 (m_flags); + i.WriteU8 (m_hopcount); + i.WriteU8 (m_ttl); + WriteTo (i, m_originatorAddress); + i.WriteHtolsbU32 (m_destSeqNumber); + i.WriteHtolsbU32 (m_metric); +} +uint8_t +IeRann::DeserializeInformation (Buffer::Iterator start, uint8_t length) +{ + Buffer::Iterator i = start; + m_flags = i.ReadU8 (); + m_hopcount = i.ReadU8 (); + m_ttl = i.ReadU8 (); + ReadFrom (i, m_originatorAddress); + m_destSeqNumber = i.ReadLsbtohU32 (); + m_metric = i.ReadLsbtohU32 (); + return i.GetDistanceFrom (start); +} +uint8_t +IeRann::GetInformationSize () const +{ + uint8_t retval = 1 //Flags + + 1 //Hopcount + + 1 //TTL + + 6 //OriginatorAddress + + 4 //DestSeqNumber + + 4;//Metric + return retval; +} + +void +IeRann::Print (std::ostream &os) const +{ + os << std::endl << "" << std::endl; + os << " flags = " << (int) m_flags << "std::endl"; + os << " hop count = " << (int) m_hopcount << "std::endl"; + os << " TTL = " << (int) m_ttl << "std::endl"; + os << " originator address = " << m_originatorAddress << "std::endl"; + os << " dst seq. number = " << m_destSeqNumber << "std::endl"; + os << " metric = " << m_metric << "std::endl"; + os << "" << std::endl; +} + +bool +operator== (const IeRann & a, const IeRann & b) +{ + return (a.m_flags == b.m_flags && a.m_hopcount == b.m_hopcount && a.m_ttl == b.m_ttl + && a.m_originatorAddress == b.m_originatorAddress && a.m_destSeqNumber == b.m_destSeqNumber + && a.m_metric == b.m_metric); +} +std::ostream & +operator << (std::ostream &os, const IeRann &a) +{ + a.Print (os); + return os; +} +} +} // namespace ns3::dot11s + + diff --git a/src/devices/mesh/dot11s/ie-dot11s-rann.h b/src/devices/mesh/dot11s/ie-dot11s-rann.h new file mode 100644 index 000000000..4fa7c1f49 --- /dev/null +++ b/src/devices/mesh/dot11s/ie-dot11s-rann.h @@ -0,0 +1,78 @@ +/* -*- 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 + */ + +#ifndef RANN_INFORMATION_ELEMENT_H +#define RANN_INFORMATION_ELEMENT_H + +#include "ns3/mac48-address.h" +#include "ns3/wifi-information-element-vector.h" + +namespace ns3 { +namespace dot11s { +/** + * \ingroup dot11s + * \brief See 7.3.2.95 of 802.11s draft 2.07 + */ +class IeRann : public WifiInformationElement +{ +public: + IeRann (); + virtual ~IeRann (); + void SetFlags (uint8_t flags); + void SetHopcount (uint8_t hopcount); + void SetTTL (uint8_t ttl); + void SetOriginatorAddress (Mac48Address originator_address); + void SetDestSeqNumber (uint32_t dest_seq_number); + void SetMetric (uint32_t metric); + uint8_t GetFlags (); + uint8_t GetHopcount (); + uint8_t GetTtl (); + Mac48Address GetOriginatorAddress (); + uint32_t GetDestSeqNumber (); + uint32_t GetMetric (); + void DecrementTtl (); + void IncrementMetric (uint32_t metric); + /** + * \name Inherited from WifiInformationElement + * \{ + */ + virtual WifiElementId ElementId () const; + virtual void SerializeInformation (Buffer::Iterator i) const; + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length); + virtual uint8_t GetInformationSize () const; + virtual void Print (std::ostream &os) const; + ///\} +private: + uint8_t m_flags; + uint8_t m_hopcount; + uint8_t m_ttl; + Mac48Address m_originatorAddress; + uint32_t m_destSeqNumber; + uint32_t m_metric; + + friend bool operator== (const IeRann & a, const IeRann & b); +}; + +bool operator== (const IeRann & a, const IeRann & b); +std::ostream &operator << (std::ostream &os, const IeRann &rann); +} // namespace dot11s +} //namespace ns3 + +#endif diff --git a/src/devices/mesh/dot11s/peer-link-frame.cc b/src/devices/mesh/dot11s/peer-link-frame.cc new file mode 100644 index 000000000..f7915a994 --- /dev/null +++ b/src/devices/mesh/dot11s/peer-link-frame.cc @@ -0,0 +1,311 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + */ + +#include "peer-link-frame.h" +#include "ie-dot11s-peer-management.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/test.h" +#include "ns3/packet.h" + +namespace ns3 { +namespace dot11s { +NS_OBJECT_ENSURE_REGISTERED (PeerLinkFrameStart); + +PeerLinkFrameStart::PeerLinkFrameStart () : + m_subtype (255), m_capability (0), m_aid (0), m_rates (SupportedRates ()), m_meshId (IeMeshId ()), + m_config (IeConfiguration ()), m_reasonCode ((uint16_t) REASON11S_RESERVED) +{ +} +void +PeerLinkFrameStart::SetPlinkFrameSubtype (uint8_t subtype) +{ + m_subtype = subtype; +} +void +PeerLinkFrameStart::SetPlinkFrameStart (PeerLinkFrameStart::PlinkFrameStartFields fields) +{ + m_subtype = fields.subtype; + m_protocol = fields.protocol; + if (m_subtype != (uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE)) + { + m_capability = fields.capability; + } + if (m_subtype == (uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM)) + { + m_aid = fields.aid; + } + if (m_subtype != (uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE)) + { + m_rates = fields.rates; + } + if (m_subtype != (uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM)) + { + m_meshId = fields.meshId; + } + if (m_subtype != (uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE)) + { + m_config = fields.config; + } + else + { + m_reasonCode = fields.reasonCode; + } +} +PeerLinkFrameStart::PlinkFrameStartFields +PeerLinkFrameStart::GetFields () +{ + PlinkFrameStartFields retval; + //TODO: protocol version: + retval.subtype = m_subtype; + retval.capability = m_capability; + retval.aid = m_aid; + retval.rates = m_rates; + retval.meshId = m_meshId; + retval.config = m_config; + retval.reasonCode = m_reasonCode; + return retval; +} +TypeId +PeerLinkFrameStart::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::PeerLinkFrameStart") .SetParent
    () .AddConstructor< + PeerLinkFrameStart> (); + return tid; +} +TypeId +PeerLinkFrameStart::GetInstanceTypeId () const +{ + return GetTypeId (); +} +void +PeerLinkFrameStart::Print (std::ostream &os) const +{ + os << "subtype = " << (uint16_t) m_subtype << std::endl << "capability = " << m_capability << std::endl << "laid = " + << (uint16_t) m_aid << std::endl << "rates = " << m_rates << std::endl << "meshId = "; + m_meshId.Print (os); + os << std::endl << "configuration = "; + m_config.Print (os); + os << std::endl << "reason code = " << m_reasonCode; +} +uint32_t +PeerLinkFrameStart::GetSerializedSize () const +{ + uint32_t size = 3; //Peering protocol + NS_ASSERT (m_subtype < 3); + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + size += 2; //capability + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) == m_subtype) + { + size += 2; //AID of remote peer + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + size += m_rates.GetSerializedSize (); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) != m_subtype) + { + size += m_meshId.GetInformationSize () + 2; + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + size += m_config.GetInformationSize () + 2; + } + else + { + size += 2; //reasonCode + } + return size; +} +void +PeerLinkFrameStart::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + NS_ASSERT (m_subtype < 3); + i.WriteU8 (IE11S_MESH_PEERING_PROTOCOL_VERSION); + i.WriteU8 (m_protocol.GetInformationSize ()); + m_protocol.SerializeInformation (i); + i.Next (m_protocol.GetInformationSize ()); + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + i.WriteHtolsbU16 (m_capability); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) == m_subtype) + { + i.WriteHtolsbU16 (m_aid); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + i = m_rates.Serialize (i); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) != m_subtype) + { + i.WriteU8 (IE11S_MESH_ID); + i.WriteU8 (m_meshId.GetInformationSize ()); + m_meshId.SerializeInformation (i); + i.Next (m_meshId.GetInformationSize ()); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + i.WriteU8 (IE11S_MESH_CONFIGURATION); + i.WriteU8 (m_config.GetInformationSize ()); + m_config.SerializeInformation (i); + i.Next (m_config.GetInformationSize ()); + } + else + { + i.WriteHtolsbU16 (m_reasonCode); + } +} +uint32_t +PeerLinkFrameStart::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + NS_ASSERT (m_subtype < 3); + { + uint8_t id = i.ReadU8 (); + uint8_t length = i.ReadU8 (); + m_protocol.DeserializeInformation (i, length); + if ((m_protocol.ElementId () != (WifiElementId) id) || (m_protocol.GetInformationSize () != length)) + { + NS_FATAL_ERROR ("Broken frame: Element ID does not match IE itself!"); + } + i.Next (m_protocol.GetInformationSize ()); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + m_capability = i.ReadLsbtohU16 (); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) == m_subtype) + { + m_aid = i.ReadLsbtohU16 (); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + i = m_rates.Deserialize (i); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM) != m_subtype) + { + uint8_t id = i.ReadU8 (); + uint8_t length = i.ReadU8 (); + m_meshId.DeserializeInformation (i, length); + if ((m_meshId.ElementId () != (WifiElementId) id) || (m_meshId.GetInformationSize () != length)) + { + NS_FATAL_ERROR ("Broken frame: Element ID does not match IE itself!"); + } + i.Next (m_meshId.GetInformationSize ()); + } + if ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE) != m_subtype) + { + uint8_t id = i.ReadU8 (); + uint8_t length = i.ReadU8 (); + m_config. DeserializeInformation (i, length); + if ((m_config.ElementId () != (WifiElementId) id) || (m_config.GetInformationSize () != length)) + { + NS_FATAL_ERROR ("Broken frame: Element ID does not match IE itself!"); + } + i.Next (m_config.GetInformationSize ()); + } + else + { + m_reasonCode = i.ReadLsbtohU16 (); + } + return i.GetDistanceFrom (start); +} +bool +operator== (const PeerLinkFrameStart & a, const PeerLinkFrameStart & b) +{ + return ((a.m_subtype == b.m_subtype) && (a.m_capability == b.m_capability) && (a.m_aid == b.m_aid) + && (a.m_meshId.IsEqual (b.m_meshId)) && (a.m_config == b.m_config) + && (a.m_reasonCode == b.m_reasonCode)); +} +#ifdef RUN_SELF_TESTS +/// Built-in self test for PeerLinkFrameStart +struct PeerLinkFrameStartBist : public Test +{ + PeerLinkFrameStartBist () : + Test ("Mesh/802.11s/PeerLinkFrameStart") + { + } + virtual bool + RunTests (); +}; +/// Test instance +static PeerLinkFrameStartBist g_PeerLinkFrameStartBist; + +bool +PeerLinkFrameStartBist::RunTests () +{ + bool result (true); + { + PeerLinkFrameStart a; + PeerLinkFrameStart::PlinkFrameStartFields fields; + fields.subtype = (uint8_t) (WifiMeshActionHeader::PEER_LINK_OPEN); + fields.capability = 0; + fields.aid = 101; + fields.reasonCode = 12; + fields.meshId = IeMeshId ("qwertyuiop"); + a.SetPlinkFrameStart (fields); + Ptr packet = Create (); + packet->AddHeader (a); + PeerLinkFrameStart b; + b.SetPlinkFrameSubtype ((uint8_t) (WifiMeshActionHeader::PEER_LINK_OPEN)); + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + { + PeerLinkFrameStart a; + PeerLinkFrameStart::PlinkFrameStartFields fields; + fields.subtype = (uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM); + fields.capability = 0; + fields.aid = 1234; + fields.reasonCode = 12; + fields.meshId = IeMeshId ("qwerty"); + a.SetPlinkFrameStart (fields); + Ptr packet = Create (); + packet->AddHeader (a); + PeerLinkFrameStart b; + b.SetPlinkFrameSubtype ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CONFIRM)); + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + { + PeerLinkFrameStart a; + PeerLinkFrameStart::PlinkFrameStartFields fields; + fields.subtype = (uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE); + fields.capability = 0; + fields.aid = 10; + fields.meshId = IeMeshId ("qqq"); + fields.reasonCode = 12; + a.SetPlinkFrameStart (fields); + Ptr packet = Create (); + packet->AddHeader (a); + PeerLinkFrameStart b; + b.SetPlinkFrameSubtype ((uint8_t) (WifiMeshActionHeader::PEER_LINK_CLOSE)); + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (a, b); + } + return result; +} +#endif +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/peer-link-frame.h b/src/devices/mesh/dot11s/peer-link-frame.h new file mode 100644 index 000000000..19bc4e0cd --- /dev/null +++ b/src/devices/mesh/dot11s/peer-link-frame.h @@ -0,0 +1,93 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + */ + +#ifndef PEER_LINK_FRAME_START_H +#define PEER_LINK_FRAME_START_H +#include "ns3/header.h" +#include "ns3/supported-rates.h" +#include "dot11s-mac-header.h" +#include "ie-dot11s-configuration.h" +#include "ie-dot11s-peering-protocol.h" +#include "ie-dot11s-id.h" +namespace ns3 +{ +class MeshWifiInterfaceMac; +namespace dot11s +{ +/** + * \ingroup dot11s + * + * \brief 802.11s Peer link management frame: + * \details included the following (see chapters 7.4.12.1-7.4.12.3 of + * 802.11s): + * - Subtype field + * - Association ID field + * - Supported rates + * - Mesh ID of mesh + */ +class PeerLinkFrameStart : public Header +{ +public: + PeerLinkFrameStart (); + ///\brief fields: + struct PlinkFrameStartFields + { + uint8_t subtype; + IePeeringProtocol protocol; //Peering protocol version - in all subtypes - 3 octets + uint16_t capability; //open and confirm + uint16_t aid; //confirm only + SupportedRates rates; //open and confirm + IeMeshId meshId; //open and close + IeConfiguration config; //open and confirm + uint16_t reasonCode; //close only + }; + ///\attention: must be set before deserialize, before only multihop + //action header knows about subtype + void SetPlinkFrameSubtype (uint8_t subtype); + void SetPlinkFrameStart (PlinkFrameStartFields); + PlinkFrameStartFields GetFields (); + /** \name Inherited from header: + * \{ + */ + static TypeId GetTypeId (); + virtual TypeId GetInstanceTypeId () const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize () const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + /** + * \} + */ +private: + uint8_t m_subtype; + IePeeringProtocol m_protocol; + uint16_t m_capability; + uint16_t m_aid; + SupportedRates m_rates; + IeMeshId m_meshId; + IeConfiguration m_config; + uint16_t m_reasonCode; + + friend bool operator== (const PeerLinkFrameStart & a, const PeerLinkFrameStart & b); +}; +bool operator== (const PeerLinkFrameStart & a, const PeerLinkFrameStart & b); +} //namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/peer-link.cc b/src/devices/mesh/dot11s/peer-link.cc new file mode 100644 index 000000000..98be146ab --- /dev/null +++ b/src/devices/mesh/dot11s/peer-link.cc @@ -0,0 +1,699 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + * Pavel Boyko + */ + +#include "peer-management-protocol-mac.h" +#include "ns3/peer-link.h" +#include "ns3/log.h" +#include "ns3/simulator.h" +#include "ns3/traced-value.h" + +NS_LOG_COMPONENT_DEFINE ("Dot11sPeerManagementProtocol"); + +namespace ns3 { +namespace dot11s { + +NS_OBJECT_ENSURE_REGISTERED( PeerLink); + +TypeId +PeerLink::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::dot11s::PeerLink") + .SetParent () + .AddConstructor () + .AddAttribute ( "RetryTimeout", + "Retry timeout", + TimeValue (TimeValue (MicroSeconds (40 * 1024))), + MakeTimeAccessor ( + &PeerLink::m_dot11MeshRetryTimeout), + MakeTimeChecker () + ) + .AddAttribute ( "HoldingTimeout", + "Holding timeout", + TimeValue (TimeValue (MicroSeconds (40 * 1024))), + MakeTimeAccessor ( + &PeerLink::m_dot11MeshHoldingTimeout), + MakeTimeChecker () + ) + .AddAttribute ( "ConfirmTimeout", + "Confirm timeout", + TimeValue (TimeValue (MicroSeconds (40 * 1024))), + MakeTimeAccessor ( + &PeerLink::m_dot11MeshConfirmTimeout), + MakeTimeChecker () + ) + .AddAttribute ( "MaxRetries", + "Maximum number of retries", + UintegerValue (4), + MakeUintegerAccessor ( + &PeerLink::m_dot11MeshMaxRetries), + MakeUintegerChecker () + ) + .AddAttribute ( "MaxBeaconLoss", + "Maximum number of lost beacons before link will be closed", + UintegerValue (2), + MakeUintegerAccessor ( + &PeerLink::m_maxBeaconLoss), + MakeUintegerChecker (1) + ) + .AddAttribute ( "MaxPacketFailure", + "Maximum number of failed packets before link will be closed", + UintegerValue (2), + MakeUintegerAccessor ( + &PeerLink::m_maxPacketFail), + MakeUintegerChecker (1) + ) + ; + return tid; +} + + +//----------------------------------------------------------------------------- +// PeerLink public interface +//----------------------------------------------------------------------------- +PeerLink::PeerLink () : + m_peerAddress (Mac48Address::GetBroadcast ()), + m_peerMeshPointAddress (Mac48Address::GetBroadcast ()), + m_localLinkId (0), + m_peerLinkId (0), + m_packetFail (0), + m_state (IDLE), + m_retryCounter (0), + m_maxPacketFail (3) +{ +} +PeerLink::~PeerLink () +{ +} +void +PeerLink::DoDispose () +{ + m_retryTimer.Cancel (); + m_holdingTimer.Cancel (); + m_confirmTimer.Cancel (); + m_beaconLossTimer.Cancel (); + m_beaconTiming.ClearTimingElement (); +} +void +PeerLink::SetPeerAddress (Mac48Address macaddr) +{ + m_peerAddress = macaddr; +} +void +PeerLink::SetPeerMeshPointAddress (Mac48Address macaddr) +{ + m_peerMeshPointAddress = macaddr; +} +void +PeerLink::SetInterface (uint32_t interface) +{ + m_interface = interface; +} +void +PeerLink::SetLocalLinkId (uint16_t id) +{ + m_localLinkId = id; +} +void +PeerLink::SetLocalAid (uint16_t aid) +{ + m_assocId = aid; +} +void +PeerLink::SetBeaconInformation (Time lastBeacon, Time beaconInterval) +{ + m_lastBeacon = lastBeacon; + m_beaconInterval = beaconInterval; + m_beaconLossTimer.Cancel (); + Time delay = Seconds (beaconInterval.GetSeconds () * m_maxBeaconLoss); + NS_ASSERT (delay.GetMicroSeconds () != 0); + m_beaconLossTimer = Simulator::Schedule (delay, &PeerLink::BeaconLoss, this); +} +void +PeerLink::MLMESetSignalStatusCallback (PeerLink::SignalStatusCallback cb) +{ + m_linkStatusCallback = cb; +} +void +PeerLink::BeaconLoss () +{ + StateMachine (CNCL); +} +void +PeerLink::TransmissionSuccess () +{ + m_packetFail = 0; +} +void +PeerLink::TransmissionFailure () +{ + m_packetFail ++; + if (m_packetFail == m_maxPacketFail) + { + StateMachine (CNCL); + m_packetFail = 0; + } +} + +void +PeerLink::SetBeaconTimingElement (IeBeaconTiming beaconTiming) +{ + m_beaconTiming = beaconTiming; +} +Mac48Address +PeerLink::GetPeerAddress () const +{ + return m_peerAddress; +} +uint16_t +PeerLink::GetLocalAid () const +{ + return m_assocId; +} +Time +PeerLink::GetLastBeacon () const +{ + return m_lastBeacon; +} +Time +PeerLink::GetBeaconInterval () const +{ + return m_beaconInterval; +} +IeBeaconTiming +PeerLink::GetBeaconTimingElement () const +{ + return m_beaconTiming; +} +void +PeerLink::MLMECancelPeerLink (PmpReasonCode reason) +{ + StateMachine (CNCL, reason); +} +void +PeerLink::MLMEActivePeerLinkOpen () +{ + StateMachine (ACTOPN); +} +void +PeerLink::MLMEPeeringRequestReject () +{ + StateMachine (REQ_RJCT, REASON11S_PEERING_CANCELLED); +} +void +PeerLink::Close (uint16_t localLinkId, uint16_t peerLinkId, PmpReasonCode reason) +{ + if (peerLinkId != 0 && m_localLinkId != peerLinkId) + { + return; + } + if (m_peerLinkId == 0) + { + m_peerLinkId = localLinkId; + } + else + { + if (m_peerLinkId != localLinkId) + { + return; + } + } + StateMachine (CLS_ACPT, reason); +} +void +PeerLink::OpenAccept (uint16_t localLinkId, IeConfiguration conf, Mac48Address peerMp) +{ + if (m_peerLinkId == 0) + { + m_peerLinkId = localLinkId; + } + m_configuration = conf; + if (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()) + { + NS_ASSERT (m_peerMeshPointAddress == peerMp); + } + else + { + m_peerMeshPointAddress = peerMp; + } + StateMachine (OPN_ACPT); +} +void +PeerLink::OpenReject (uint16_t localLinkId, IeConfiguration conf, Mac48Address peerMp, PmpReasonCode reason) +{ + if (m_peerLinkId == 0) + { + m_peerLinkId = localLinkId; + } + m_configuration = conf; + if (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()) + { + NS_ASSERT (m_peerMeshPointAddress == peerMp); + } + else + { + m_peerMeshPointAddress = peerMp; + } + StateMachine (OPN_RJCT, reason); +} +void +PeerLink::ConfirmAccept (uint16_t localLinkId, uint16_t peerLinkId, uint16_t peerAid, IeConfiguration conf, + Mac48Address peerMp) +{ + if (m_localLinkId != peerLinkId) + { + return; + } + if (m_peerLinkId == 0) + { + m_peerLinkId = localLinkId; + } + else + { + if (m_peerLinkId != localLinkId) + { + return; + } + } + m_configuration = conf; + m_peerAssocId = peerAid; + if (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()) + { + NS_ASSERT (m_peerMeshPointAddress == peerMp); + } + else + { + m_peerMeshPointAddress = peerMp; + } + StateMachine (CNF_ACPT); +} +void +PeerLink::ConfirmReject (uint16_t localLinkId, uint16_t peerLinkId, IeConfiguration conf, + Mac48Address peerMp, PmpReasonCode reason) +{ + if (m_localLinkId != peerLinkId) + { + return; + } + if (m_peerLinkId == 0) + { + m_peerLinkId = localLinkId; + } + else + { + if (m_peerLinkId != localLinkId) + { + return; + } + } + m_configuration = conf; + if (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()) + { + NS_ASSERT (m_peerMeshPointAddress == peerMp); + } + m_peerMeshPointAddress = peerMp; + StateMachine (CNF_RJCT, reason); +} +bool +PeerLink::LinkIsEstab () const +{ + return (m_state == ESTAB); +} +bool +PeerLink::LinkIsIdle () const +{ + return (m_state == IDLE); +} +void +PeerLink::SetMacPlugin (Ptr plugin) +{ + m_macPlugin = plugin; +} +//----------------------------------------------------------------------------- +// Private +//----------------------------------------------------------------------------- +void +PeerLink::StateMachine (PeerEvent event, PmpReasonCode reasoncode) +{ + switch (m_state) + { + case IDLE: + switch (event) + { + case CNCL: + case CLS_ACPT: + m_state = IDLE; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, IDLE, IDLE); + break; + case REQ_RJCT: + SendPeerLinkClose (reasoncode); + break; + case ACTOPN: + m_state = OPN_SNT; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, IDLE, OPN_SNT); + SendPeerLinkOpen (); + SetRetryTimer (); + break; + case OPN_ACPT: + m_state = OPN_RCVD; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, IDLE, OPN_RCVD); + SendPeerLinkConfirm (); + SendPeerLinkOpen (); + SetRetryTimer (); + break; + default: + //11B.5.3.4 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + case OPN_SNT: + switch (event) + { + case TOR1: + SendPeerLinkOpen (); + m_retryCounter++; + SetRetryTimer (); + break; + case CNF_ACPT: + m_state = CNF_RCVD; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, CNF_RCVD); + ClearRetryTimer (); + SetConfirmTimer (); + break; + case OPN_ACPT: + m_state = OPN_RCVD; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, OPN_RCVD); + SendPeerLinkConfirm (); + break; + case CLS_ACPT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_MESH_CLOSE_RCVD); + SetHoldingTimer (); + break; + case OPN_RJCT: + case CNF_RJCT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (reasoncode); + SetHoldingTimer (); + break; + case TOR2: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_MESH_MAX_RETRIES); + SetHoldingTimer (); + break; + case CNCL: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_SNT, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_PEERING_CANCELLED); + SetHoldingTimer (); + break; + default: + //11B.5.3.5 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + case CNF_RCVD: + switch (event) + { + case CNF_ACPT: + break; + case OPN_ACPT: + m_state = ESTAB; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, CNF_RCVD, ESTAB); + ClearConfirmTimer (); + SendPeerLinkConfirm (); + NS_ASSERT (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()); + break; + case CLS_ACPT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, CNF_RCVD, HOLDING); + ClearConfirmTimer (); + SendPeerLinkClose (REASON11S_MESH_CLOSE_RCVD); + SetHoldingTimer (); + break; + case CNF_RJCT: + case OPN_RJCT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, CNF_RCVD, HOLDING); + ClearConfirmTimer (); + SendPeerLinkClose (reasoncode); + SetHoldingTimer (); + break; + case CNCL: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, CNF_RCVD, HOLDING); + ClearConfirmTimer (); + SendPeerLinkClose (REASON11S_PEERING_CANCELLED); + SetHoldingTimer (); + break; + case TOC: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, CNF_RCVD, HOLDING); + SendPeerLinkClose (REASON11S_MESH_CONFIRM_TIMEOUT); + SetHoldingTimer (); + break; + default: + //11B.5.3.6 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + case OPN_RCVD: + switch (event) + { + case TOR1: + SendPeerLinkOpen (); + m_retryCounter++; + SetRetryTimer (); + break; + case CNF_ACPT: + m_state = ESTAB; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_RCVD, ESTAB); + ClearRetryTimer (); + NS_ASSERT (m_peerMeshPointAddress != Mac48Address::GetBroadcast ()); + break; + case CLS_ACPT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_RCVD, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_MESH_CLOSE_RCVD); + SetHoldingTimer (); + break; + case OPN_RJCT: + case CNF_RJCT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_RCVD, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (reasoncode); + SetHoldingTimer (); + break; + case TOR2: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_RCVD, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_MESH_MAX_RETRIES); + SetHoldingTimer (); + break; + case CNCL: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, OPN_RCVD, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (REASON11S_PEERING_CANCELLED); + SetHoldingTimer (); + break; + default: + //11B.5.3.7 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + case ESTAB: + switch (event) + { + case OPN_ACPT: + SendPeerLinkConfirm (); + break; + case CLS_ACPT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, ESTAB, HOLDING); + SendPeerLinkClose (REASON11S_MESH_CLOSE_RCVD); + SetHoldingTimer (); + break; + case OPN_RJCT: + case CNF_RJCT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, ESTAB, HOLDING); + ClearRetryTimer (); + SendPeerLinkClose (reasoncode); + SetHoldingTimer (); + break; + case CNCL: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, ESTAB, HOLDING); + SendPeerLinkClose (REASON11S_PEERING_CANCELLED); + SetHoldingTimer (); + break; + default: + //11B.5.3.8 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + case HOLDING: + switch (event) + { + case CLS_ACPT: + ClearHoldingTimer (); + case TOH: + m_state = IDLE; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, HOLDING, IDLE); + break; + case OPN_ACPT: + case CNF_ACPT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, HOLDING, HOLDING); + // reason not spec in D2.0 + SendPeerLinkClose (REASON11S_PEERING_CANCELLED); + break; + case OPN_RJCT: + case CNF_RJCT: + m_state = HOLDING; + m_linkStatusCallback (m_interface, m_peerAddress, m_peerMeshPointAddress, HOLDING, HOLDING); + SendPeerLinkClose (reasoncode); + break; + default: + //11B.5.3.9 of 802.11s Draft D3.0 + //All other events shall be ignored in this state + break; + } + break; + } +} +void +PeerLink::ClearRetryTimer () +{ + m_retryTimer.Cancel (); +} +void +PeerLink::ClearConfirmTimer () +{ + m_confirmTimer.Cancel (); +} +void +PeerLink::ClearHoldingTimer () +{ + m_holdingTimer.Cancel (); +} +void +PeerLink::SendPeerLinkClose (PmpReasonCode reasoncode) +{ + IePeerManagement peerElement; + peerElement.SetPeerClose (m_localLinkId, m_peerLinkId, reasoncode); + m_macPlugin->SendPeerLinkManagementFrame (m_peerAddress, m_peerMeshPointAddress, m_assocId, peerElement, + m_configuration); +} +void +PeerLink::SendPeerLinkOpen () +{ + IePeerManagement peerElement; + peerElement.SetPeerOpen (m_localLinkId); + NS_ASSERT (m_macPlugin != 0); + m_macPlugin->SendPeerLinkManagementFrame (m_peerAddress, m_peerMeshPointAddress, m_assocId, peerElement, + m_configuration); +} +void +PeerLink::SendPeerLinkConfirm () +{ + IePeerManagement peerElement; + peerElement.SetPeerConfirm (m_localLinkId, m_peerLinkId); + m_macPlugin->SendPeerLinkManagementFrame (m_peerAddress, m_peerMeshPointAddress, m_assocId, peerElement, + m_configuration); +} +void +PeerLink::SetHoldingTimer () +{ + NS_ASSERT (m_dot11MeshHoldingTimeout.GetMicroSeconds () != 0); + m_holdingTimer = Simulator::Schedule (m_dot11MeshHoldingTimeout, &PeerLink::HoldingTimeout, this); +} +void +PeerLink::HoldingTimeout () +{ + StateMachine (TOH); +} +void +PeerLink::SetRetryTimer () +{ + NS_ASSERT (m_dot11MeshRetryTimeout.GetMicroSeconds () != 0); + m_retryTimer = Simulator::Schedule (m_dot11MeshRetryTimeout, &PeerLink::RetryTimeout, this); +} +void +PeerLink::RetryTimeout () +{ + if (m_retryCounter < m_dot11MeshMaxRetries) + { + StateMachine (TOR1); + } + else + { + StateMachine (TOR2); + } +} +void +PeerLink::SetConfirmTimer () +{ + NS_ASSERT (m_dot11MeshConfirmTimeout.GetMicroSeconds () != 0); + m_confirmTimer = Simulator::Schedule (m_dot11MeshConfirmTimeout, &PeerLink::ConfirmTimeout, this); +} +void +PeerLink::ConfirmTimeout () +{ + StateMachine (TOC); +} +void +PeerLink::Report (std::ostream & os) const +{ + if (m_state != ESTAB) + { + return; + } + os << "GetAddress () << "\"" << std::endl << + "peerInterfaceAddress=\"" << m_peerAddress << "\"" << std::endl << + "peerMeshPointAddress=\"" << m_peerMeshPointAddress << "\"" << std::endl << + "metric=\"" << m_macPlugin->GetLinkMetric (m_peerAddress) << "\"" << std::endl << + "lastBeacon=\"" << m_lastBeacon.GetSeconds () << "\"" << std::endl << + "localLinkId=\"" << m_localLinkId << "\"" << std::endl << + "peerLinkId=\"" << m_peerLinkId << "\"" << std::endl << + "assocId=\"" << m_assocId << "\"" << std::endl << + "/>" << std::endl; +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/peer-link.h b/src/devices/mesh/dot11s/peer-link.h new file mode 100644 index 000000000..e8b65c4a4 --- /dev/null +++ b/src/devices/mesh/dot11s/peer-link.h @@ -0,0 +1,259 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#ifndef PEERLLINK_H_ +#define PEERLLINK_H_ + +#include "ns3/nstime.h" +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/mac48-address.h" +#include "ns3/event-id.h" +#include "ns3/ie-dot11s-beacon-timing.h" +#include "ns3/ie-dot11s-peer-management.h" +#include "ns3/ie-dot11s-configuration.h" +namespace ns3 { +namespace dot11s { +class PeerManagementProtocolMac; +/** + * \ingroup dot11s + * + * \brief Peer link model for 802.11s Peer Management protocol + */ +class PeerLink : public Object +{ +public: + friend class PeerManagementProtocol; + /// Support object system + static TypeId GetTypeId (); + /// C-tor create empty link + PeerLink (); + ~PeerLink (); + void DoDispose (); + /// Peer Link state: + enum PeerState { + IDLE, + OPN_SNT, + CNF_RCVD, + OPN_RCVD, + ESTAB, + HOLDING, + }; + /// Process beacon received from peer + void SetBeaconInformation (Time lastBeacon, Time BeaconInterval); + /** + * \brief Method used to detect peer link changes + * + * \param cb is a callback, which notifyes, that on interface (uint32_t), peer link + * with address (Mac48Address) was opened (bool is true) or closed (bool is false) + */ + void SetLinkStatusCallback (Callback cb); + /** + * \name Peer link getters/setters + * \{ + */ + void SetPeerAddress (Mac48Address macaddr); + void SetPeerMeshPointAddress (Mac48Address macaddr); + void SetInterface (uint32_t interface); + void SetLocalLinkId (uint16_t id); + void SetPeerLinkId (uint16_t id); + void SetLocalAid (uint16_t aid); + void SetPeerAid (uint16_t aid); + void SetBeaconTimingElement (IeBeaconTiming beaconTiming); + void SetPeerLinkDescriptorElement (IePeerManagement peerLinkElement); + Mac48Address GetPeerAddress () const; + uint16_t GetLocalAid () const; + Time GetLastBeacon () const; + Time GetBeaconInterval () const; + IeBeaconTiming GetBeaconTimingElement ()const; + IePeerManagement GetPeerLinkDescriptorElement ()const; + //\} + + /** + * \name MLME + * \{ + */ + /// MLME-CancelPeerLink.request + void MLMECancelPeerLink (PmpReasonCode reason); + /// MLME-ActivePeerLinkOpen.request + void MLMEActivePeerLinkOpen (); + /// MLME-PeeringRequestReject + void MLMEPeeringRequestReject (); + /// Callback type for MLME-SignalPeerLinkStatus event + typedef Callback SignalStatusCallback; + /// Set callback + void MLMESetSignalStatusCallback (SignalStatusCallback); + /// Reports about transmission success/failure + void TransmissionSuccess (); + void TransmissionFailure (); + //\} + ///\brief Statistics + void Report (std::ostream & os) const; +private: + /// Peer link events, see 802.11s draft 11B.3.3.2 + enum PeerEvent + { + CNCL, ///< Cancel peer link + ACTOPN, ///< Active peer link open + CLS_ACPT, ///< PeerLinkClose_Accept + OPN_ACPT, ///< PeerLinkOpen_Accept + OPN_RJCT, ///< PeerLinkOpen_Reject + REQ_RJCT, ///< PeerLinkOpenReject by internal reason + CNF_ACPT, ///< PeerLinkConfirm_Accept + CNF_RJCT, ///< PeerLinkConfirm_Reject + TOR1, ///< Timeout of retry timer + TOR2, ///< also timeout of retry timer + TOC, ///< Timeout of confirm timer + TOH, ///< Timeout of holding (gracefull closing) timer + }; + /// State transition + void StateMachine (PeerEvent event, PmpReasonCode = REASON11S_RESERVED); + /** + * \name Link response to received management frames + * + * \attention In all this methods {local/peer}LinkID correspond to _peer_ station, as written in + * received frame, e.g. I am peerLinkID and peer link is localLinkID . + * + * \{ + */ + /// Close link + void Close (uint16_t localLinkID, uint16_t peerLinkID, PmpReasonCode reason); + /// Accept open link + void OpenAccept (uint16_t localLinkId, IeConfiguration conf, Mac48Address peerMp); + /// Reject open link + void OpenReject (uint16_t localLinkId, IeConfiguration conf, Mac48Address peerMp, PmpReasonCode reason); + /// Confirm accept + void ConfirmAccept ( + uint16_t localLinkId, + uint16_t peerLinkId, + uint16_t peerAid, + IeConfiguration conf, + Mac48Address peerMp + ); + /// Confirm reject + void ConfirmReject ( + uint16_t localLinkId, + uint16_t peerLinkId, + IeConfiguration conf, + Mac48Address peerMp, + PmpReasonCode reason + ); + //\} + /// True if link is established + bool LinkIsEstab () const; + /// True if link is idle. Link can be deleted in this state + bool LinkIsIdle () const; + /** + * Set pointer to MAC-plugin, which is responsible for sending peer + * link management frames + */ + void SetMacPlugin (Ptr plugin); + /** + * \name Event handlers + * \{ + */ + void ClearRetryTimer (); + void ClearConfirmTimer (); + void ClearHoldingTimer (); + void SetHoldingTimer (); + void SetRetryTimer (); + void SetConfirmTimer (); + //\} + + /** + * \name Work with management frames + * \{ + */ + void SendPeerLinkClose (PmpReasonCode reasoncode); + void SendPeerLinkOpen (); + void SendPeerLinkConfirm (); + //\} + + /** + * \name Timeout handlers + * \{ + */ + void HoldingTimeout (); + void RetryTimeout (); + void ConfirmTimeout (); + //\} + /// Several successive beacons were lost, close link + void BeaconLoss (); +private: + ///The number of interface I am associated with + uint32_t m_interface; + /// pointer to MAC plugin, which is responsible for peer management + Ptr m_macPlugin; + /// Peer address + Mac48Address m_peerAddress; + /// Mesh point address, equal to peer address in case of single + //interface mesh point + Mac48Address m_peerMeshPointAddress; + /// My ID of this link + uint16_t m_localLinkId; + /// Peer ID of this link + uint16_t m_peerLinkId; + /// My association ID + uint16_t m_assocId; + /// Assoc Id assigned to me by peer + uint16_t m_peerAssocId; + + /// When last beacon was received + Time m_lastBeacon; + /// Current beacon interval on corresponding interface + Time m_beaconInterval; + /// How many successive packets were failed to transmit + uint16_t m_packetFail; + + /// Current state + PeerState m_state; + /** + * \brief Mesh interface configuration + * \attention Is not used now, nothing to configure :) + */ + IeConfiguration m_configuration; + /// Beacon timing element received from the peer. Needed by BCA + IeBeaconTiming m_beaconTiming; + + /** + * \name Timers & counters used for internal state transitions + * \{ + */ + uint16_t m_dot11MeshMaxRetries; + Time m_dot11MeshRetryTimeout; + Time m_dot11MeshHoldingTimeout; + Time m_dot11MeshConfirmTimeout; + + EventId m_retryTimer; + EventId m_holdingTimer; + EventId m_confirmTimer; + uint16_t m_retryCounter; + EventId m_beaconLossTimer; + uint16_t m_maxBeaconLoss; + uint16_t m_maxPacketFail; + //\} + /// How to report my status change + SignalStatusCallback m_linkStatusCallback; +}; + +} // namespace dot11s +} //namespace ns3 +#endif /* PEERLLINK_H_ */ diff --git a/src/devices/mesh/dot11s/peer-management-protocol-mac.cc b/src/devices/mesh/dot11s/peer-management-protocol-mac.cc new file mode 100644 index 000000000..f1277aab8 --- /dev/null +++ b/src/devices/mesh/dot11s/peer-management-protocol-mac.cc @@ -0,0 +1,332 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#include "ie-dot11s-configuration.h" +#include "ie-dot11s-peer-management.h" +#include "dot11s-mac-header.h" +#include "peer-management-protocol-mac.h" +#include "peer-management-protocol.h" +#include "peer-link-frame.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/simulator.h" +#include "ns3/wifi-mac-header.h" +#include "ns3/wifi-information-element-vector.h" +#include "ns3/log.h" +namespace ns3 { +namespace dot11s { +PeerManagementProtocolMac::PeerManagementProtocolMac (uint32_t interface, + Ptr protocol) +{ + m_ifIndex = interface; + m_protocol = protocol; +} + +PeerManagementProtocolMac::~PeerManagementProtocolMac () +{ +} + +void +PeerManagementProtocolMac::SetParent (Ptr parent) +{ + m_parent = parent; + m_parent->TraceConnectWithoutContext ("TxErrHeader", MakeCallback (&PeerManagementProtocolMac::TxError, this)); + m_parent->TraceConnectWithoutContext ("TxOkHeader", MakeCallback (&PeerManagementProtocolMac::TxOk, this)); +} +void +PeerManagementProtocolMac::TxError (WifiMacHeader const &hdr) +{ + m_protocol->TransmissionFailure (m_ifIndex, hdr.GetAddr1 ()); +} +void +PeerManagementProtocolMac::TxOk (WifiMacHeader const &hdr) +{ + m_protocol->TransmissionSuccess (m_ifIndex, hdr.GetAddr1 ()); +} +bool +PeerManagementProtocolMac::Receive (Ptr const_packet, const WifiMacHeader & header) +{ + // First of all we copy a packet, because we need to remove some + //headers + Ptr packet = const_packet->Copy (); + if (header.IsBeacon ()) + { + MgtBeaconHeader beacon_hdr; + packet->RemoveHeader (beacon_hdr); + //meshId.FindFirst (myBeacon); + bool meshBeacon = false; + WifiInformationElementVector elements; + packet->RemoveHeader(elements); + Ptr beaconTiming = DynamicCast (elements.FindFirst (IE11S_BEACON_TIMING)); + Ptr meshId = DynamicCast (elements.FindFirst (IE11S_MESH_ID)); + + if ((beaconTiming != 0) && (meshId != 0)) + { + if (m_protocol->GetMeshId ()->IsEqual (*meshId)) + { + meshBeacon = true; + } + } + m_protocol->UpdatePeerBeaconTiming (m_ifIndex, meshBeacon, *beaconTiming, header.GetAddr2 (), + Simulator::Now (), MicroSeconds (beacon_hdr.GetBeaconIntervalUs ())); + // Beacon shall not be dropeed. May be needed to another plugins + return true; + } + if (header.IsAction ()) + { + WifiMeshActionHeader actionHdr; + packet->RemoveHeader (actionHdr); + WifiMeshActionHeader::ActionValue actionValue = actionHdr.GetAction (); + // If can not handle - just return; + if (actionHdr.GetCategory () != WifiMeshActionHeader::MESH_PEERING_MGT) + { + return m_protocol->IsActiveLink (m_ifIndex, header.GetAddr2 ()); + } + m_stats.rxMgt++; + m_stats.rxMgtBytes += packet->GetSize (); + Mac48Address peerAddress = header.GetAddr2 (); + Mac48Address peerMpAddress = header.GetAddr3 (); + PeerLinkFrameStart::PlinkFrameStartFields fields; + { + PeerLinkFrameStart peerFrame; + peerFrame.SetPlinkFrameSubtype ((uint8_t) actionValue.peerLink); + packet->RemoveHeader (peerFrame); + fields = peerFrame.GetFields (); + NS_ASSERT (fields.subtype == actionValue.peerLink); + } + if ((actionValue.peerLink != WifiMeshActionHeader::PEER_LINK_CLOSE) && !(m_parent->CheckSupportedRates ( + fields.rates))) + { + m_protocol->ConfigurationMismatch (m_ifIndex, peerAddress); + // Broken peer link frame - drop it + m_stats.brokenMgt++; + return false; + } + if ((actionValue.peerLink != WifiMeshActionHeader::PEER_LINK_CONFIRM) && !fields.meshId.IsEqual ( + *(m_protocol->GetMeshId ()))) + { + m_protocol->ConfigurationMismatch (m_ifIndex, peerAddress); + // Broken peer link frame - drop it + m_stats.brokenMgt++; + return false; + } + Ptr peerElement; + //Peer Management element is the last element in this frame - so, we can use WifiInformationElementVector + WifiInformationElementVector elements; + packet->RemoveHeader (elements); + peerElement = DynamicCast(elements.FindFirst(IE11S_PEERING_MANAGEMENT)); + NS_ASSERT (peerElement != 0); + //Check taht frame subtype corresponds peer link subtype + if (peerElement->SubtypeIsOpen ()) + { + m_stats.rxOpen++; + NS_ASSERT (actionValue.peerLink == WifiMeshActionHeader::PEER_LINK_OPEN); + } + if (peerElement->SubtypeIsConfirm ()) + { + m_stats.rxConfirm++; + NS_ASSERT (actionValue.peerLink == WifiMeshActionHeader::PEER_LINK_CONFIRM); + } + if (peerElement->SubtypeIsClose ()) + { + m_stats.rxClose++; + NS_ASSERT (actionValue.peerLink == WifiMeshActionHeader::PEER_LINK_CLOSE); + } + //Deliver Peer link management frame to protocol: + m_protocol->ReceivePeerLinkFrame (m_ifIndex, peerAddress, peerMpAddress, fields.aid, *peerElement, + fields.config); + // if we can handle a frame - drop it + return false; + } + return m_protocol->IsActiveLink (m_ifIndex, header.GetAddr2 ()); +} +bool +PeerManagementProtocolMac::UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, + Mac48Address from, Mac48Address to) +{ + if (header.IsAction ()) + { + WifiMeshActionHeader actionHdr; + packet->PeekHeader (actionHdr); + WifiMeshActionHeader::ActionValue actionValue = actionHdr.GetAction (); + if (actionHdr.GetCategory () == WifiMeshActionHeader::MESH_PEERING_MGT) + { + return true; + } + } + if (header.GetAddr1 ().IsGroup ()) + { + return true; + } + else + { + if (m_protocol->IsActiveLink (m_ifIndex, header.GetAddr1 ())) + { + return true; + } + else + { + m_stats.dropped++; + return false; + } + } +} +void +PeerManagementProtocolMac::UpdateBeacon (MeshWifiBeacon & beacon) const +{ + Ptr beaconTiming = m_protocol->GetBeaconTimingElement (m_ifIndex); + beacon.AddInformationElement (beaconTiming); + beacon.AddInformationElement (m_protocol->GetMeshId ()); +} + +void +PeerManagementProtocolMac::SendPeerLinkManagementFrame (Mac48Address peerAddress, Mac48Address peerMpAddress, + uint16_t aid, IePeerManagement peerElement, IeConfiguration meshConfig) +{ + //Create a packet: + meshConfig.SetNeighborCount (m_protocol->GetNumberOfLinks ()); + Ptr packet = Create (); + WifiInformationElementVector elements; + elements.AddInformationElement(Ptr (&peerElement)); + packet->AddHeader (elements); + PeerLinkFrameStart::PlinkFrameStartFields fields; + fields.rates = m_parent->GetSupportedRates (); + fields.capability = 0; + fields.meshId = *(m_protocol->GetMeshId ()); + fields.config = meshConfig; + PeerLinkFrameStart plinkFrame; + //Create an 802.11 frame header: + //Send management frame to MAC: + WifiMeshActionHeader actionHdr; + if (peerElement.SubtypeIsOpen ()) + { + m_stats.txOpen++; + WifiMeshActionHeader::ActionValue action; + action.peerLink = WifiMeshActionHeader::PEER_LINK_OPEN; + fields.subtype = WifiMeshActionHeader::PEER_LINK_OPEN; + actionHdr.SetAction (WifiMeshActionHeader::MESH_PEERING_MGT, action); + } + if (peerElement.SubtypeIsConfirm ()) + { + m_stats.txConfirm++; + WifiMeshActionHeader::ActionValue action; + action.peerLink = WifiMeshActionHeader::PEER_LINK_CONFIRM; + fields.aid = aid; + fields.subtype = WifiMeshActionHeader::PEER_LINK_CONFIRM; + actionHdr.SetAction (WifiMeshActionHeader::MESH_PEERING_MGT, action); + } + if (peerElement.SubtypeIsClose ()) + { + m_stats.txClose++; + WifiMeshActionHeader::ActionValue action; + action.peerLink = WifiMeshActionHeader::PEER_LINK_CLOSE; + fields.subtype = WifiMeshActionHeader::PEER_LINK_CLOSE; + fields.reasonCode = peerElement.GetReasonCode (); + actionHdr.SetAction (WifiMeshActionHeader::MESH_PEERING_MGT, action); + } + plinkFrame.SetPlinkFrameStart (fields); + packet->AddHeader (plinkFrame); + packet->AddHeader (actionHdr); + m_stats.txMgt++; + m_stats.txMgtBytes += packet->GetSize (); + // Wifi Mac header: + WifiMacHeader hdr; + hdr.SetAction (); + hdr.SetAddr1 (peerAddress); + hdr.SetAddr2 (m_parent->GetAddress ()); + //Addr is not used here, we use it as our MP address + hdr.SetAddr3 (m_protocol->GetAddress ()); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + m_parent->SendManagementFrame (packet, hdr); +} + +Mac48Address +PeerManagementProtocolMac::GetAddress () const +{ + if (m_parent != 0) + { + return m_parent->GetAddress (); + } + else + { + return Mac48Address::Mac48Address (); + } +} +std::pair +PeerManagementProtocolMac::GetBeaconInfo () const +{ + std::pair retval; + retval.first = m_parent->GetTbtt (); + retval.second = m_parent->GetBeaconInterval (); + return retval; +} +void +PeerManagementProtocolMac::SetBeaconShift (Time shift) +{ + if (shift != Seconds (0)) + { + m_stats.beaconShift++; + } + m_parent->ShiftTbtt (shift); +} +PeerManagementProtocolMac::Statistics::Statistics () : + txOpen (0), txConfirm (0), txClose (0), rxOpen (0), rxConfirm (0), rxClose (0), dropped (0), brokenMgt (0), + txMgt (0), txMgtBytes (0), rxMgt (0), rxMgtBytes (0), beaconShift (0) +{ +} +void +PeerManagementProtocolMac::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; +} +void +PeerManagementProtocolMac::Report (std::ostream & os) const +{ + os << "GetAddress () << "\">" << std::endl; + m_stats.Print (os); + os << "" << std::endl; +} +void +PeerManagementProtocolMac::ResetStats () +{ + m_stats = Statistics::Statistics (); +} +uint32_t +PeerManagementProtocolMac::GetLinkMetric (Mac48Address peerAddress) +{ + return m_parent->GetLinkMetric (peerAddress); +} +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/peer-management-protocol-mac.h b/src/devices/mesh/dot11s/peer-management-protocol-mac.h new file mode 100644 index 000000000..c61d27c86 --- /dev/null +++ b/src/devices/mesh/dot11s/peer-management-protocol-mac.h @@ -0,0 +1,139 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#ifndef PEER_MANAGER_MAC_PLUGIN_H_ +#define PEER_MANAGER_MAC_PLUGIN_H_ + +#include "ns3/mesh-wifi-interface-mac-plugin.h" + +namespace ns3 { +class MeshWifiInterfaceMac; +namespace dot11s { +class PeerManagementProtocol; +class IeConfiguration; +class IePeerManagement; +class PeerManagementProtocol; +/** + * \ingroup dot11s + * + * \brief This is plugin to Mesh WiFi MAC, which implements + * interface to dot11s peer management protocol: it takes proper + * frames from MAC-layer, extracts peer link management information + * element and mesh configuration element and passes it to main part + * of protocol + */ +class PeerManagementProtocolMac : public MeshWifiInterfaceMacPlugin +{ +public: + PeerManagementProtocolMac (uint32_t interface, Ptr protocol); + ~PeerManagementProtocolMac (); + ///\name Inherited from plugin abstract class + ///\{ + void SetParent (Ptr parent); + bool Receive (Ptr packet, const WifiMacHeader & header); + bool UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, Mac48Address to); + void UpdateBeacon (MeshWifiBeacon & beacon) const; + ///\} + ///\name Statistics: + ///\{ + void Report (std::ostream &) const; + void ResetStats (); + uint32_t GetLinkMetric (Mac48Address peerAddress); + ///\} +private: + friend class PeerManagementProtocol; + friend class PeerLink; + ///\name Create peer link management frames: + ///\{ + struct PlinkFrameStart + { + uint8_t subtype; + uint16_t aid; + SupportedRates rates; + uint16_t qos; + }; + Ptr CreatePeerLinkOpenFrame (); + Ptr CreatePeerLinkConfirmFrame (); + Ptr CreatePeerLinkCloseFrame (); + /** + * \brief This structure keeps all fields in peer link management frame, + * which are not subclasses of WifiInformationElement + */ + /// \name Parses the start of the frame, where there are no + /// WifiInformationElements exist + PlinkFrameStart ParsePlinkFrame (Ptr packet); + ///\} + ///// Closes link when a proper number of successive transmissions have failed + void TxError (WifiMacHeader const &hdr); + void TxOk (WifiMacHeader const &hdr); + ///\name BCA functionallity: + ///\{ + ///\brief Fills TBTT and beacon interval. Needed by BCA + ///functionallity + ///\param first in retval is TBTT + ///\param second in retval is beacon interval + std::pair GetBeaconInfo () const; + void SetBeaconShift (Time shift); + ///\} + void SetPeerManagerProtcol (Ptr protocol); + void SendPeerLinkManagementFrame ( + Mac48Address peerAddress, + Mac48Address peerMpAddress, + uint16_t aid, + IePeerManagement peerElement, + IeConfiguration meshConfig + ); + ///\brief DUBUG only - to print established links + Mac48Address GetAddress () const; + ///\name Statistics + struct Statistics + { + uint16_t txOpen; + uint16_t txConfirm; + uint16_t txClose; + uint16_t rxOpen; + uint16_t rxConfirm; + uint16_t rxClose; + uint16_t dropped; + uint16_t brokenMgt; + uint16_t txMgt; + uint32_t txMgtBytes; + uint16_t rxMgt; + uint32_t rxMgtBytes; + uint16_t beaconShift; + + Statistics (); + void Print (std::ostream & os) const; + }; +private: + struct Statistics m_stats; + ///\} + ///\name Information about MAC and protocol: + ///\{ + Ptr m_parent; + uint32_t m_ifIndex; + Ptr m_protocol; + ///\} +}; + +} // namespace dot11s +} //namespace ns3 +#endif + diff --git a/src/devices/mesh/dot11s/peer-management-protocol.cc b/src/devices/mesh/dot11s/peer-management-protocol.cc new file mode 100644 index 000000000..fe144f0e5 --- /dev/null +++ b/src/devices/mesh/dot11s/peer-management-protocol.cc @@ -0,0 +1,576 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#include "ns3/peer-management-protocol.h" +#include "peer-management-protocol-mac.h" +#include "ie-dot11s-configuration.h" +#include "ie-dot11s-id.h" +#include "ns3/mesh-point-device.h" +#include "ns3/simulator.h" +#include "ns3/assert.h" +#include "ns3/log.h" +#include "ns3/random-variable.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/mesh-wifi-interface-mac-plugin.h" +#include "ns3/wifi-net-device.h" + +NS_LOG_COMPONENT_DEFINE ("PeerManagementProtocol"); +namespace ns3 { +namespace dot11s { +/*************************************************** + * PeerManager + ***************************************************/ +NS_OBJECT_ENSURE_REGISTERED (PeerManagementProtocol); + +TypeId +PeerManagementProtocol::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::dot11s::PeerManagementProtocol") + .SetParent () + .AddConstructor () + // maximum number of peer links. Now we calculate the total + // number of peer links on all interfaces + .AddAttribute ( "MaxNumberOfPeerLinks", + "Maximum number of peer links", + UintegerValue (32), + MakeUintegerAccessor ( + &PeerManagementProtocol::m_maxNumberOfPeerLinks), + MakeUintegerChecker () + ) + .AddAttribute ( "MaxBeaconLossForBeaconTiming", + "If maximum number of beacons were lost, station will not included in beacon timing element", + UintegerValue (3), + MakeUintegerAccessor ( + &PeerManagementProtocol::m_maxBeaconLostForBeaconTiming), + MakeUintegerChecker () + ) + ; + return tid; +} +PeerManagementProtocol::PeerManagementProtocol () : + m_lastAssocId (0), m_lastLocalLinkId (1), m_maxBeaconLostForBeaconTiming (3) +{ +} +PeerManagementProtocol::~PeerManagementProtocol () +{ +} +void +PeerManagementProtocol::DoDispose () +{ + //cancel cleanup event and go through the map of peer links, + //deleting each + for (PeerLinksMap::iterator j = m_peerLinks.begin (); j != m_peerLinks.end (); j++) + { + for (PeerLinksOnInterface::iterator i = j->second.begin (); i != j->second.end (); i++) + { + (*i) = 0; + } + j->second.clear (); + } + m_peerLinks.clear (); + //cleaning beacon structures: + for (BeaconInfoMap::iterator i = m_neighbourBeacons.begin (); i != m_neighbourBeacons.end (); i++) + { + i->second.clear (); + } + m_neighbourBeacons.clear (); +} + +bool +PeerManagementProtocol::Install (Ptr mp) +{ + std::vector > interfaces = mp->GetInterfaces (); + for (std::vector >::iterator i = interfaces.begin (); i != interfaces.end (); i++) + { + Ptr wifiNetDev = (*i)->GetObject (); + if (wifiNetDev == 0) + { + return false; + } + Ptr mac = wifiNetDev->GetMac ()->GetObject (); + if (mac == 0) + { + return false; + } + Ptr plugin = Create ((*i)->GetIfIndex (), this); + mac->InstallPlugin (plugin); + m_plugins[(*i)->GetIfIndex ()] = plugin; + PeerLinksOnInterface newmap; + m_peerLinks[(*i)->GetIfIndex ()] = newmap; + } + // Mesh point aggregates all installed protocols + m_address = Mac48Address::ConvertFrom (mp->GetAddress ()); + mp->AggregateObject (this); + return true; +} + +Ptr +PeerManagementProtocol::GetBeaconTimingElement (uint32_t interface) +{ + Ptr retval = Create (); + BeaconInfoMap::iterator i = m_neighbourBeacons.find (interface); + if (i == m_neighbourBeacons.end ()) + { + return retval; + } + bool cleaned = false; + while (!cleaned) + { + BeaconsOnInterface::iterator start = i->second.begin (); + for (BeaconsOnInterface::iterator j = start; j != i->second.end (); j++) + { + //check beacon loss and make a timing element + //if last beacon was m_maxBeaconLostForBeaconTiming beacons ago - we do not put it to the + //timing element + if ((j->second.referenceTbtt + j->second.beaconInterval * Scalar (m_maxBeaconLostForBeaconTiming)) + < Simulator::Now ()) + { + start = j; + i->second.erase (j); + break; + } + } + cleaned = true; + } + for (BeaconsOnInterface::const_iterator j = i->second.begin (); j != i->second.end (); j++) + { + retval->AddNeighboursTimingElementUnit (j->second.aid, j->second.referenceTbtt, + j->second.beaconInterval); + } + return retval; +} + +void +PeerManagementProtocol::FillBeaconInfo (uint32_t interface, Mac48Address peerAddress, Time receivingTime, + Time beaconInterval) +{ + BeaconInfoMap::iterator i = m_neighbourBeacons.find (interface); + if (i == m_neighbourBeacons.end ()) + { + BeaconsOnInterface newMap; + m_neighbourBeacons[interface] = newMap; + } + i = m_neighbourBeacons.find (interface); + BeaconsOnInterface::iterator j = i->second.find (peerAddress); + if (j == i->second.end ()) + { + BeaconInfo newInfo; + newInfo.referenceTbtt = receivingTime; + newInfo.beaconInterval = beaconInterval; + newInfo.aid = m_lastAssocId++; + if (m_lastAssocId == 0xff) + { + m_lastAssocId = 0; + } + i->second[peerAddress] = newInfo; + } + else + { + j->second.referenceTbtt = receivingTime; + j->second.beaconInterval = beaconInterval; + } +} + +void +PeerManagementProtocol::UpdatePeerBeaconTiming (uint32_t interface, bool meshBeacon, + IeBeaconTiming timingElement, Mac48Address peerAddress, Time receivingTime, Time beaconInterval) +{ + FillBeaconInfo (interface, peerAddress, receivingTime, beaconInterval); + if (!meshBeacon) + { + return; + } + //BCA: + PeerManagementProtocolMacMap::iterator plugin = m_plugins.find (interface); + NS_ASSERT (plugin != m_plugins.end ()); + Time shift = GetNextBeaconShift (interface); + if (TimeToTu (shift) != 0) + { + plugin->second->SetBeaconShift (shift); + } + //PM STATE Machine + //Check that a given beacon is not from our interface + for (PeerManagementProtocolMacMap::const_iterator i = m_plugins.begin (); i != m_plugins.end (); i++) + { + if (i->second->GetAddress () == peerAddress) + { + return; + } + } + Ptr peerLink = FindPeerLink (interface, peerAddress); + if (peerLink != 0) + { + peerLink->SetBeaconTimingElement (timingElement); + peerLink->SetBeaconInformation (receivingTime, beaconInterval); + } + else + { + if (ShouldSendOpen (interface, peerAddress)) + { + peerLink = InitiateLink (interface, peerAddress, Mac48Address::GetBroadcast (), receivingTime, + beaconInterval); + peerLink->SetBeaconTimingElement (timingElement); + peerLink->MLMEActivePeerLinkOpen (); + } + } +} + +void +PeerManagementProtocol::ReceivePeerLinkFrame (uint32_t interface, Mac48Address peerAddress, + Mac48Address peerMeshPointAddress, uint16_t aid, IePeerManagement peerManagementElement, + IeConfiguration meshConfig) +{ + Ptr peerLink = FindPeerLink (interface, peerAddress); + if (peerManagementElement.SubtypeIsOpen ()) + { + PmpReasonCode reasonCode; + bool reject = !(ShouldAcceptOpen (interface, peerAddress, reasonCode)); + if (peerLink == 0) + { + peerLink = InitiateLink (interface, peerAddress, peerMeshPointAddress, Simulator::Now (), Seconds ( + 1.0)); + } + if (!reject) + { + peerLink->OpenAccept (peerManagementElement.GetLocalLinkId (), meshConfig, peerMeshPointAddress); + } + else + { + peerLink->OpenReject (peerManagementElement.GetLocalLinkId (), meshConfig, peerMeshPointAddress, + reasonCode); + } + } + if (peerLink == 0) + { + return; + } + if (peerManagementElement.SubtypeIsConfirm ()) + { + peerLink->ConfirmAccept (peerManagementElement.GetLocalLinkId (), + peerManagementElement.GetPeerLinkId (), aid, meshConfig, peerMeshPointAddress); + } + if (peerManagementElement.SubtypeIsClose ()) + { + peerLink->Close (peerManagementElement.GetLocalLinkId (), peerManagementElement.GetPeerLinkId (), + peerManagementElement.GetReasonCode ()); + } +} +void +PeerManagementProtocol::ConfigurationMismatch (uint32_t interface, Mac48Address peerAddress) +{ + Ptr peerLink = FindPeerLink (interface, peerAddress); + if (peerLink != 0) + { + peerLink->MLMECancelPeerLink (REASON11S_MESH_CAPABILITY_POLICY_VIOLATION); + } +} +void +PeerManagementProtocol::TransmissionFailure (uint32_t interface, Mac48Address peerAddress) +{ + NS_LOG_DEBUG("transmission failed between "< peerLink = FindPeerLink(interface, peerAddress); + if (peerLink != 0) + { + peerLink->TransmissionFailure (); + } +} +void +PeerManagementProtocol::TransmissionSuccess (uint32_t interface, Mac48Address peerAddress) +{ + NS_LOG_DEBUG("transmission success "< peerLink = FindPeerLink(interface, peerAddress); + if (peerLink != 0) + { + peerLink->TransmissionSuccess (); + } +} +Ptr +PeerManagementProtocol::InitiateLink (uint32_t interface, Mac48Address peerAddress, + Mac48Address peerMeshPointAddress, Time lastBeacon, Time beaconInterval) +{ + Ptr new_link = CreateObject (); + if (m_lastLocalLinkId == 0xff) + { + m_lastLocalLinkId = 0; + } + //find a beacon entry + BeaconInfoMap::iterator beaconsOnInterface = m_neighbourBeacons.find (interface); + if (beaconsOnInterface == m_neighbourBeacons.end ()) + { + FillBeaconInfo (interface, peerAddress, lastBeacon, beaconInterval); + } + beaconsOnInterface = m_neighbourBeacons.find (interface); + BeaconsOnInterface::iterator beacon = beaconsOnInterface->second.find (peerAddress); + if (beacon == beaconsOnInterface->second.end ()) + { + FillBeaconInfo (interface, peerAddress, lastBeacon, beaconInterval); + } + beacon = beaconsOnInterface->second.find (peerAddress); + //find a peer link - it must not exist + if (FindPeerLink (interface, peerAddress) != 0) + { + NS_FATAL_ERROR ("Peer link must not exist."); + } + // Plugin must exist + PeerManagementProtocolMacMap::iterator plugin = m_plugins.find (interface); + NS_ASSERT (plugin != m_plugins.end ()); + PeerLinksMap::iterator iface = m_peerLinks.find (interface); + NS_ASSERT (iface != m_peerLinks.end ()); + new_link->SetLocalAid (beacon->second.aid); + new_link->SetInterface (interface); + new_link->SetLocalLinkId (m_lastLocalLinkId++); + new_link->SetPeerAddress (peerAddress); + new_link->SetPeerMeshPointAddress (peerMeshPointAddress); + new_link->SetBeaconInformation (lastBeacon, beaconInterval); + new_link->SetMacPlugin (plugin->second); + new_link->MLMESetSignalStatusCallback (MakeCallback (&PeerManagementProtocol::PeerLinkStatus, this)); + iface->second.push_back (new_link); + return new_link; +} +Ptr +PeerManagementProtocol::FindPeerLink (uint32_t interface, Mac48Address peerAddress) +{ + PeerLinksMap::iterator iface = m_peerLinks.find (interface); + NS_ASSERT (iface != m_peerLinks.end ()); + for (PeerLinksOnInterface::iterator i = iface->second.begin (); i != iface->second.end (); i++) + { + if ((*i)->GetPeerAddress () == peerAddress) + { + if ((*i)->LinkIsIdle ()) + { + (*i) = 0; + (iface->second).erase (i); + return 0; + } + else + { + return (*i); + } + } + } + return 0; +} +void +PeerManagementProtocol::SetPeerLinkStatusCallback ( + Callback cb) +{ + m_peerStatusCallback = cb; +} +std::vector +PeerManagementProtocol::GetActiveLinks (uint32_t interface) +{ + std::vector retval; + PeerLinksMap::iterator iface = m_peerLinks.find (interface); + NS_ASSERT (iface != m_peerLinks.end ()); + for (PeerLinksOnInterface::iterator i = iface->second.begin (); i != iface->second.end (); i++) + { + if ((*i)->LinkIsEstab ()) + { + retval.push_back ((*i)->GetPeerAddress ()); + } + } + return retval; +} +bool +PeerManagementProtocol::IsActiveLink (uint32_t interface, Mac48Address peerAddress) +{ + Ptr peerLink = FindPeerLink (interface, peerAddress); + if (peerLink != 0) + { + return (peerLink->LinkIsEstab ()); + } + return false; +} +bool +PeerManagementProtocol::ShouldSendOpen (uint32_t interface, Mac48Address peerAddress) +{ + return (m_stats.linksTotal <= m_maxNumberOfPeerLinks); +} +bool +PeerManagementProtocol::ShouldAcceptOpen (uint32_t interface, Mac48Address peerAddress, + PmpReasonCode & reasonCode) +{ + if (m_stats.linksTotal > m_maxNumberOfPeerLinks) + { + reasonCode = REASON11S_MESH_MAX_PEERS; + return false; + } + return true; +} +Time +PeerManagementProtocol::GetNextBeaconShift (uint32_t interface) +{ + //REMINDER:: in timing element 1) last beacon reception time is measured in units of 256 microseconds + // 2) beacon interval is mesured in units of 1024 microseconds + // 3) hereafter TU = 1024 microseconds + //So, the shift is a random integer variable uniformly distributed in [-15;-1] U [1;15] + static int maxShift = 15; + static int minShift = 1; + UniformVariable randomSign (-1, 1); + UniformVariable randomShift (minShift, maxShift); + PeerLinksMap::iterator iface = m_peerLinks.find (interface); + NS_ASSERT (iface != m_peerLinks.end ()); + PeerManagementProtocolMacMap::iterator plugin = m_plugins.find (interface); + NS_ASSERT (plugin != m_plugins.end ()); + std::pair myBeacon = plugin->second->GetBeaconInfo (); + if (Simulator::Now () + TuToTime (maxShift) > myBeacon.first + myBeacon.second) + { + return MicroSeconds (0); + } + for (PeerLinksOnInterface::iterator i = iface->second.begin (); i != iface->second.end (); i++) + { + IeBeaconTiming::NeighboursTimingUnitsList neighbours; + if ((*i)->LinkIsIdle ()) + { + continue; + } + neighbours = (*i)->GetBeaconTimingElement ().GetNeighboursTimingElementsList (); + //Going through all my timing elements and detecting future beacon collisions + for (IeBeaconTiming::NeighboursTimingUnitsList::const_iterator j = neighbours.begin (); j + != neighbours.end (); j++) + { + //We apply MBAC only if beacon Intervals are equal + if ((*j)->GetBeaconInterval () == TimeToTu (myBeacon.second)) + { + //Apply MBCA if future beacons may coinside + if ((TimeToTu (myBeacon.first) - ((*j)->GetLastBeacon () / 4)) % ((*j)->GetBeaconInterval ()) + == 0) + { + int beaconShift = randomShift.GetInteger (minShift, maxShift) * ((randomSign.GetValue () + >= 0) ? 1 : -1); + NS_LOG_DEBUG ("Apply MBCA: Shift value = " << beaconShift << " beacon TUs"); + //Do not shift to the past! + return (TuToTime (beaconShift) + Simulator::Now () < myBeacon.first) ? TuToTime ( + beaconShift) : TuToTime (0); + } + } + } + } + return MicroSeconds (0); +} +Time +PeerManagementProtocol::TuToTime (uint32_t x) +{ + return MicroSeconds (x * 1024); +} +uint32_t +PeerManagementProtocol::TimeToTu (Time x) +{ + return (uint32_t) (x.GetMicroSeconds () / 1024); +} +void +PeerManagementProtocol::PeerLinkStatus (uint32_t interface, Mac48Address peerAddress, + Mac48Address peerMeshPointAddress, PeerLink::PeerState ostate, PeerLink::PeerState nstate) +{ + PeerManagementProtocolMacMap::iterator plugin = m_plugins.find (interface); + NS_ASSERT (plugin != m_plugins.end ()); + NS_LOG_DEBUG ("Link between me:" << m_address << " my interface:" << plugin->second->GetAddress () + << " and peer mesh point:" << peerMeshPointAddress << " and its interface:" << peerAddress + << ", at my interface ID:" << interface << ". State movement:" << ostate << " -> " << nstate); + if ((nstate == PeerLink::ESTAB) && (ostate != PeerLink::ESTAB)) + { + m_stats.linksOpened++; + m_stats.linksTotal++; + if (!m_peerStatusCallback.IsNull ()) + { + m_peerStatusCallback (peerMeshPointAddress, peerAddress, interface, true); + } + } + if ((ostate == PeerLink::ESTAB) && (nstate != PeerLink::ESTAB)) + { + m_stats.linksClosed++; + m_stats.linksTotal--; + if (!m_peerStatusCallback.IsNull ()) + { + m_peerStatusCallback (peerMeshPointAddress, peerAddress, interface, false); + } + } + if (nstate == PeerLink::IDLE) + { + Ptr link = FindPeerLink (interface, peerAddress); + NS_ASSERT (link == 0); + } +} +uint8_t +PeerManagementProtocol::GetNumberOfLinks () +{ + return m_stats.linksTotal; +} +Ptr +PeerManagementProtocol::GetMeshId () const +{ + NS_ASSERT (m_meshId != 0); + return m_meshId; +} +void +PeerManagementProtocol::SetMeshId (std::string s) +{ + m_meshId = Create (s); +} +Mac48Address +PeerManagementProtocol::GetAddress () +{ + return m_address; +} +PeerManagementProtocol::Statistics::Statistics (uint16_t t) : + linksTotal (t), linksOpened (0), linksClosed (0) +{ +} +void +PeerManagementProtocol::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; +} +void +PeerManagementProtocol::Report (std::ostream & os) const +{ + os << "" << std::endl; + m_stats.Print (os); + for (PeerManagementProtocolMacMap::const_iterator plugins = m_plugins.begin (); plugins != m_plugins.end (); plugins++) + { + //Take statistics from plugin: + plugins->second->Report (os); + //Print all active peer links: + PeerLinksMap::const_iterator iface = m_peerLinks.find (plugins->second->m_ifIndex); + NS_ASSERT (iface != m_peerLinks.end ()); + for (PeerLinksOnInterface::const_iterator i = iface->second.begin (); i != iface->second.end (); i++) + { + (*i)->Report (os); + } + } + os << "" << std::endl; +} +void +PeerManagementProtocol::ResetStats () +{ + m_stats = Statistics::Statistics (m_stats.linksTotal); // don't reset number of links + for (PeerManagementProtocolMacMap::const_iterator plugins = m_plugins.begin (); plugins != m_plugins.end (); plugins++) + { + plugins->second->ResetStats (); + } +} + +} // namespace dot11s +} //namespace ns3 + diff --git a/src/devices/mesh/dot11s/peer-management-protocol.h b/src/devices/mesh/dot11s/peer-management-protocol.h new file mode 100644 index 000000000..164da90fa --- /dev/null +++ b/src/devices/mesh/dot11s/peer-management-protocol.h @@ -0,0 +1,255 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Aleksey Kovalenko + */ + +#ifndef DOT11S_PEER_MAN_H +#define DOT11S_PEER_MAN_H + +#include "ns3/mac48-address.h" +#include "ns3/net-device.h" +#include "ns3/event-id.h" +#include "ns3/nstime.h" +#include "ie-dot11s-beacon-timing.h" +#include "ie-dot11s-peer-management.h" +#include "peer-link.h" + +#include +namespace ns3 { +class MeshPointDevice; +namespace dot11s { +class PeerManagementProtocolMac; +class PeerLink; +class IeMeshId; +class IePeerManagement; +class IeConfiguration; +/** + * \ingroup dot11s + * + * \brief 802.11s Peer Management Protocol model + */ +class PeerManagementProtocol : public Object +{ +public: + PeerManagementProtocol (); + ~PeerManagementProtocol (); + static TypeId GetTypeId (); + void DoDispose (); + /** + * \brief Install PMP on given mesh point. + * + * Installing protocol cause installing its interface MAC plugins. + * + * Also MP aggregates all installed protocols, PMP protocol can be accessed + * via MeshPointDevice::GetObject(); + */ + bool Install (Ptr); + /** + * \brief Methods that handle beacon sending/receiving procedure. + * + * \name This methods interact with MAC_layer plug-in + * \{ + */ + /** + * \brief When we are sending a beacon - we fill beacon timing + * element + * \return IeBeaconTiming is a beacon timing element that should be present in beacon + * \param interface is a interface sending a beacon + */ + Ptr GetBeaconTimingElement (uint32_t interface); + /** + * \brief When we receive a beacon from peer-station, we remember + * its beacon timing element (needed for peer choosing mechanism), + * and remember beacon timers - last beacon and beacon interval to + * detect beacon loss and cancel links + * \param interface is a interface on which beacon was received + * \param meshBeacon indicates whether the beacon is mesh beacon or not. + * \param timingElement is a timing element of remote beacon + * \param peerAddress is an address where a beacon was received from + * \param receivingTime is a time when beacon was received + * \param beaconInterval is a beacon interval of received beacon + */ + void UpdatePeerBeaconTiming ( + uint32_t interface, + bool meshBeacon, + IeBeaconTiming timingElement, + Mac48Address peerAddress, + Time receivingTime, + Time beaconInterval + ); + //\} + /** + * \brief Methods that handle Peer link management frames + * interaction: + * \{ + */ + /** + * Deliver Peer link management information to the protocol-part + * \param interface is a interface ID of a given MAC (interfaceID rather + * than MAC address, because many interfaces may have the same MAC) + * \param peerAddress is address of peer + * \param peerMeshPointAddress is address of peer mesh point device (equal + * to peer address when only one interface) + * \param aid is association ID, which peer has assigned to us + * \param peerManagementElement is peer link management element + * \param meshConfig is mesh configuration element taken from the peer + * management frame + */ + void ReceivePeerLinkFrame ( + uint32_t interface, + Mac48Address peerAddress, + Mac48Address peerMeshPointAddress, + uint16_t aid, + IePeerManagement peerManagementElement, + IeConfiguration meshConfig + ); + /** + * \brief Cancels peer link due to broken configuration (Mesh ID or Supported + * rates) + */ + void ConfigurationMismatch (uint32_t interface, Mac48Address peerAddress); + /** + * \brief Cancels peer link due to successive transmission failures + */ + void TransmissionFailure (uint32_t interface, const Mac48Address peerAddress); + /** + * \brief resets transmission failure statistics + */ + void TransmissionSuccess (uint32_t interface, const Mac48Address peerAddress); + /** + * \brief Checks if there is established link + */ + bool IsActiveLink (uint32_t interface, Mac48Address peerAddress); + //\} + ///\brief Needed by external module to do MLME + Ptr FindPeerLink (uint32_t interface, Mac48Address peerAddress); + void SetPeerLinkStatusCallback (Callback cb); + std::vector GetActiveLinks (uint32_t interface); + ///\brief needed by plugins to set global source address + Mac48Address GetAddress (); + ///\brief Needed to fill mesh configuration + uint8_t GetNumberOfLinks (); + void SetMeshId (std::string s); + Ptr GetMeshId () const; + ///\brief: Report statistics + void Report (std::ostream &) const; + void ResetStats (); +private: + /** \name Private structures + * \{ + */ + /// Keeps information about beacon of peer station: beacon interval, association ID, last time we have received a beacon + struct BeaconInfo + { + uint16_t aid; //Assoc ID + Time referenceTbtt; //When one of my station's beacons was put into a beacon queue; + Time beaconInterval; //Beacon interval of my station; + }; + /// We keep a vector of pointers to PeerLink class. This vector + /// keeps all peer links at a given interface. + typedef std::vector > PeerLinksOnInterface; + /// This map keeps all peer links. + typedef std::map PeerLinksMap; + /// This map keeps relationship between peer address and its beacon information + typedef std::map BeaconsOnInterface; + ///\brief This map keeps beacon information on all intefaces + typedef std::map BeaconInfoMap; + ///\brief this vector keeps pointers to MAC-plugins + typedef std::map > PeerManagementProtocolMacMap; + ///\} +private: + /** + * \brief Fills information of received beacon. Needed to form own beacon timing element + */ + void FillBeaconInfo (uint32_t interface, Mac48Address peerAddress, Time receivingTime, Time beaconInterval); + Ptr InitiateLink ( + uint32_t interface, + Mac48Address peerAddress, + Mac48Address peerMeshPointAddress, + Time lastBeacon, + Time beaconInterval + ); + /** + * \name External peer-chooser + * \{ + */ + bool ShouldSendOpen (uint32_t interface, Mac48Address peerAddress); + bool ShouldAcceptOpen (uint32_t interface, Mac48Address peerAddress, PmpReasonCode & reasonCode); + /** + * \} + * \brief Indicates changes in peer links + */ + void PeerLinkStatus (uint32_t interface, Mac48Address peerAddress, Mac48Address peerMeshPointAddres, PeerLink::PeerState ostate, PeerLink::PeerState nstate); + ///\brief BCA + Time GetNextBeaconShift (uint32_t interface); + /** + * \name Time<-->TU converters: + * \{ + */ + Time TuToTime (uint32_t x); + uint32_t TimeToTu (Time x); + ///\} +private: + PeerManagementProtocolMacMap m_plugins; + Mac48Address m_address; + Ptr m_meshId; + /** + * \name Information related to beacons: + * \{ + */ + BeaconInfoMap m_neighbourBeacons; + ///\} + uint16_t m_lastAssocId; + uint16_t m_lastLocalLinkId; + uint8_t m_maxNumberOfPeerLinks; + uint8_t m_maxBeaconLostForBeaconTiming; + /** + * \name Peer Links + * \{ + */ + PeerLinksMap m_peerLinks; + /** + * \} + */ + /** + * \brief Callback to notify about peer link changes: + * Mac48Address is peer address of mesh point, + * Mac48Address is peer address of interface, + * uint32_t - interface ID, + * bool is status - true when new link has appeared, false - when link was closed, + */ + Callback m_peerStatusCallback; + ///\} + ///\name Statistics: + ///\{ + struct Statistics { + uint16_t linksTotal; + uint16_t linksOpened; + uint16_t linksClosed; + + Statistics (uint16_t t = 0); + void Print (std::ostream & os) const; + }; + struct Statistics m_stats; + ///\} +}; + +} // namespace dot11s +} //namespace ns3 +#endif diff --git a/src/devices/mesh/dot11s/waf b/src/devices/mesh/dot11s/waf new file mode 100755 index 000000000..0ca1fc3f4 --- /dev/null +++ b/src/devices/mesh/dot11s/waf @@ -0,0 +1 @@ +exec "`dirname "$0"`"/../../../../waf "$@" diff --git a/src/devices/mesh/dot11s/wscript b/src/devices/mesh/dot11s/wscript new file mode 100644 index 000000000..62cd0391f --- /dev/null +++ b/src/devices/mesh/dot11s/wscript @@ -0,0 +1,36 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + obj = bld.create_ns3_module('dot11s', ['wifi', 'mesh']) + obj.source = [ + 'ie-dot11s-beacon-timing.cc', + 'ie-dot11s-configuration.cc', + 'ie-dot11s-id.cc', + 'ie-dot11s-peer-management.cc', + 'ie-dot11s-preq.cc', + 'ie-dot11s-prep.cc', + 'ie-dot11s-perr.cc', + 'ie-dot11s-rann.cc', + 'ie-dot11s-peering-protocol.cc', + 'ie-dot11s-metric-report.cc', + 'dot11s-mac-header.cc', + 'peer-link-frame.cc', + 'peer-link.cc', + 'peer-management-protocol-mac.cc', + 'peer-management-protocol.cc', + 'hwmp-tag.cc', + 'hwmp-rtable.cc', + 'hwmp-protocol-mac.cc', + 'hwmp-protocol.cc', + 'airtime-metric.cc', + ] + headers = bld.new_task_gen('ns3header') + headers.module = 'dot11s' + headers.source = [ + 'hwmp-protocol.h', + 'peer-management-protocol.h', + 'ie-dot11s-beacon-timing.h', + 'ie-dot11s-configuration.h', + 'ie-dot11s-peer-management.h', + 'peer-link.h', + ] diff --git a/src/devices/mesh/flame/flame-header.cc b/src/devices/mesh/flame/flame-header.cc new file mode 100644 index 000000000..8deb86103 --- /dev/null +++ b/src/devices/mesh/flame/flame-header.cc @@ -0,0 +1,182 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ +#include "ns3/assert.h" +#include "ns3/address-utils.h" +#include "ns3/packet.h" +#include "ns3/test.h" + +#include "flame-header.h" + +namespace ns3 { +namespace flame { +FlameHeader::FlameHeader () : + m_cost (0), m_seqno (0), m_origDst (Mac48Address ()), m_origSrc (Mac48Address ()) +{ +} +FlameHeader::~FlameHeader () +{ +} +TypeId +FlameHeader::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::FlameHeader") + .SetParent
    () + .AddConstructor (); + return tid; +} +TypeId +FlameHeader::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} +void +FlameHeader::Print (std::ostream &os) const +{ + os << "Cost = " << (uint16_t) m_cost << std::endl << "Sequence number = " << m_seqno + << std::endl << "Orig Destination = " << m_origDst << std::endl << "Orig Source = " << m_origSrc << std::endl; +} +uint32_t +FlameHeader::GetSerializedSize (void) const +{ + return 1 // Reserved + + 1 // Cost + + 2 // Seqno + + 6 // Orig Dst + + 6 // Orig Src + + 2 // Flame Port + ; +} +void +FlameHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteU8 (0); //Reserved + i.WriteU8 (m_cost); //Cost + i.WriteHtonU16 (m_seqno); //Seqno + WriteTo (i, m_origDst); + WriteTo (i, m_origSrc); + i.WriteHtonU16 (m_protocol); +} +uint32_t +FlameHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + i.Next (1); + m_cost = i.ReadU8 (); + m_seqno = i.ReadNtohU16 (); + ReadFrom (i, m_origDst); + ReadFrom (i, m_origSrc); + m_protocol = i.ReadNtohU16 (); + return i.GetDistanceFrom (start); +} +void +FlameHeader::AddCost (uint8_t cost) +{ + m_cost = (((uint16_t) cost + (uint16_t) m_cost) > 255) ? 255 : cost + m_cost; +} +uint8_t +FlameHeader::GetCost () const +{ + return m_cost; +} +void +FlameHeader::SetSeqno (uint16_t seqno) +{ + m_seqno = seqno; +} +uint16_t +FlameHeader::GetSeqno () const +{ + return m_seqno; +} +void +FlameHeader::SetOrigDst (Mac48Address dst) +{ + m_origDst = dst; +} +Mac48Address +FlameHeader::GetOrigDst () const +{ + return m_origDst; +} +void +FlameHeader::SetOrigSrc (Mac48Address src) +{ + m_origSrc = src; +} +Mac48Address +FlameHeader::GetOrigSrc () const +{ + return m_origSrc; +} +void +FlameHeader::SetProtocol (uint16_t protocol) +{ + m_protocol = protocol; +} +uint16_t +FlameHeader::GetProtocol () const +{ + return m_protocol; +} +bool +operator== (const FlameHeader & a, const FlameHeader & b) +{ + return ((a.m_cost == b.m_cost) && (a.m_seqno == b.m_seqno) && (a.m_origDst == b.m_origDst) && (a.m_origSrc + == b.m_origSrc) && (a.m_protocol == b.m_protocol)); +} + +#ifdef RUN_SELF_TESTS + +/// Built-in self test for FlameHeader +struct FlameHeaderBist : public Test +{ + FlameHeaderBist () : + Test ("Mesh/Flame/FlameHeader") + { + } + virtual bool + RunTests (); +}; + +/// Test instance +static FlameHeaderBist g_FlameHeaderBist; + +bool +FlameHeaderBist::RunTests () +{ + bool result (true); + FlameHeader a; + a.AddCost (123); + a.SetSeqno (456); + a.SetOrigDst (Mac48Address ("11:22:33:44:55:66")); + a.SetOrigSrc (Mac48Address ("00:11:22:33:44:55")); + a.SetProtocol (0x806); + Ptr packet = Create (); + packet->AddHeader (a); + FlameHeader b; + packet->RemoveHeader (b); + NS_TEST_ASSERT_EQUAL (b, a); + return result; +} +#endif + +} //namespace flame +} // namespace ns3 diff --git a/src/devices/mesh/flame/flame-header.h b/src/devices/mesh/flame/flame-header.h new file mode 100644 index 000000000..8879ea2b3 --- /dev/null +++ b/src/devices/mesh/flame/flame-header.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#ifndef FLAME_HEADER_H +#define FLAME_HEADER_H + +#include "ns3/header.h" +#include "ns3/mac48-address.h" + +namespace ns3 { +namespace flame { +/** + * \ingroup flame + * + * \brief Flame header + * + * Header format: | Reserved: 1 | cost: 1 | Sequence number: 2 | OrigDst: 6 | OrigSrc: 6 | Flame port : 2 | + */ + +class FlameHeader : public Header +{ +public: + + FlameHeader (); + ~FlameHeader (); + /** + * \name Inherited from Header class: + * \{ + */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + ///\} + /** + * \name Seeters/Getters for fields: + * \{ + */ + void AddCost (uint8_t cost); + uint8_t GetCost () const; + void SetSeqno (uint16_t seqno); + uint16_t GetSeqno () const; + void SetOrigDst (Mac48Address dst); + Mac48Address GetOrigDst () const; + void SetOrigSrc (Mac48Address OrigSrc); + Mac48Address GetOrigSrc () const; + void SetProtocol (uint16_t protocol); + uint16_t GetProtocol () const; + ///\} +private: + uint8_t m_cost; + uint16_t m_seqno; + Mac48Address m_origDst; + Mac48Address m_origSrc; + uint16_t m_protocol; + friend bool operator== (const FlameHeader & a, const FlameHeader & b); +}; +bool operator== (const FlameHeader & a, const FlameHeader & b); +} //namespace flame +} //namespace ns3 +#endif /* FLAME_HEADER_H */ diff --git a/src/devices/mesh/flame/flame-protocol-mac.cc b/src/devices/mesh/flame/flame-protocol-mac.cc new file mode 100644 index 000000000..f9b6650c5 --- /dev/null +++ b/src/devices/mesh/flame/flame-protocol-mac.cc @@ -0,0 +1,128 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#include "flame-protocol-mac.h" +#include "flame-protocol.h" +#include "flame-header.h" +#include "ns3/log.h" +namespace ns3 { +namespace flame { +NS_LOG_COMPONENT_DEFINE ("FlameProtocolMac"); +FlameProtocolMac::FlameProtocolMac (uint32_t ifIndex, Ptr protocol) : + m_protocol (protocol), m_ifIndex (ifIndex) +{ +} +FlameProtocolMac::~FlameProtocolMac () +{ +} +void +FlameProtocolMac::SetParent (Ptr parent) +{ + m_parent = parent; +} + +bool +FlameProtocolMac::Receive (Ptr packet, const WifiMacHeader & header) +{ + if (!header.IsData ()) + { + return true; + } + FlameTag tag; + if (packet->PeekPacketTag (tag)) + { + NS_FATAL_ERROR ("FLAME tag is not supposed to be received by network"); + } + tag.receiver = header.GetAddr1 (); + tag.transmitter = header.GetAddr2 (); + if (tag.receiver == Mac48Address::GetBroadcast ()) + { + m_stats.rxBroadcast++; + } + else + { + m_stats.rxUnicast++; + } + m_stats.rxBytes += packet->GetSize (); + packet->AddPacketTag (tag); + return true; +} +bool +FlameProtocolMac::UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, + Mac48Address to) +{ + if (!header.IsData ()) + { + return true; + } + FlameTag tag; + if (!packet->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("FLAME tag must exist here"); + } + header.SetAddr1 (tag.receiver); + if (tag.receiver == Mac48Address::GetBroadcast ()) + { + m_stats.txBroadcast++; + } + else + { + m_stats.txUnicast++; + } + m_stats.txBytes += packet->GetSize (); + return true; +} +uint16_t +FlameProtocolMac::GetChannelId () const +{ + return m_parent->GetFrequencyChannel (); +} +FlameProtocolMac::Statistics::Statistics () : + txUnicast (0), txBroadcast (0), txBytes (0), rxUnicast (0), rxBroadcast (0), rxBytes (0) +{ +} +void +FlameProtocolMac::Statistics::Print (std::ostream &os) const +{ + os << "" << std::endl; +} +void +FlameProtocolMac::Report (std::ostream & os) const +{ + os << "GetAddress () << "\">" << std::endl; + m_stats.Print (os); + os << "" << std::endl; + +} +void +FlameProtocolMac::ResetStats () +{ + m_stats = Statistics (); +} + +} //namespace flame +} //namespace ns3 diff --git a/src/devices/mesh/flame/flame-protocol-mac.h b/src/devices/mesh/flame/flame-protocol-mac.h new file mode 100644 index 000000000..63de914fd --- /dev/null +++ b/src/devices/mesh/flame/flame-protocol-mac.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#ifndef FLAME_PROTOCOL_MAC_H +#define FLAME_PROTOCOL_MAC_H + +#include "ns3/mesh-wifi-interface-mac.h" + +namespace ns3 { +namespace flame { +class FlameProtocol; +/** + * \ingroup flame + * + * \brief Interface MAC plugin FLAME routing protocol + */ +class FlameProtocolMac : public MeshWifiInterfaceMacPlugin +{ +public: + FlameProtocolMac (uint32_t, Ptr); + ~FlameProtocolMac (); + ///\name Inherited from MAC plugin + //\{ + void SetParent (Ptr parent); + bool Receive (Ptr packet, const WifiMacHeader & header); + bool UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, Mac48Address to); + /// Update beacon is empty, because HWMP does not know anything about beacons + void UpdateBeacon (MeshWifiBeacon & beacon) const {}; + //\} + uint16_t GetChannelId () const; + /// Report statistics + void Report (std::ostream &) const; + void ResetStats (); +private: + /** + * \name MeshPointDevice parameters: + * \{ + */ + Ptr m_protocol; + uint32_t m_ifIndex; + Ptr m_parent; + ///\} + ///\name Statistics: + ///\{ + struct Statistics + { + uint16_t txUnicast; + uint16_t txBroadcast; + uint32_t txBytes; + uint16_t rxUnicast; + uint16_t rxBroadcast; + uint32_t rxBytes; + + void Print (std::ostream & os) const; + Statistics (); + }; + Statistics m_stats; + ///\} + +}; +} //namespace flame +} //namespace ns3 +#endif /* FLAME_PROTOCOL_MAC_H */ diff --git a/src/devices/mesh/flame/flame-protocol.cc b/src/devices/mesh/flame/flame-protocol.cc new file mode 100644 index 000000000..d0bd251f0 --- /dev/null +++ b/src/devices/mesh/flame/flame-protocol.cc @@ -0,0 +1,377 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#include "flame-protocol.h" +#include "flame-protocol-mac.h" +#include "flame-header.h" +#include "flame-rtable.h" +#include "ns3/llc-snap-header.h" +#include "ns3/log.h" +#include "ns3/simulator.h" +#include "ns3/packet.h" +#include "ns3/mesh-point-device.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-point-device.h" +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/random-variable.h" + +NS_LOG_COMPONENT_DEFINE ("FlameProtocol"); + +namespace ns3 { +namespace flame { +//----------------------------------------------------------------------------- +// FlameTag +//----------------------------------------------------------------------------- +NS_OBJECT_ENSURE_REGISTERED (FlameTag); + +TypeId +FlameTag::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::flame::FlameTag") .SetParent () .AddConstructor (); + return tid; +} + +TypeId +FlameTag::GetInstanceTypeId () const +{ + return GetTypeId (); +} + +uint32_t +FlameTag::GetSerializedSize () const +{ + return 12; +} + +void +FlameTag::Serialize (TagBuffer i) const +{ + uint8_t buf[6]; + receiver.CopyTo (buf); + for (int j = 0; j < 6; j++) + { + i.WriteU8 (buf[j]); + } + transmitter.CopyTo (buf); + for (int j = 0; j < 6; j++) + { + i.WriteU8 (buf[j]); + } + +} + +void +FlameTag::Deserialize (TagBuffer i) +{ + uint8_t buf[6]; + for (int j = 0; j < 6; j++) + { + buf[j] = i.ReadU8 (); + } + receiver.CopyFrom (buf); + for (int j = 0; j < 6; j++) + { + buf[j] = i.ReadU8 (); + } + transmitter.CopyFrom (buf); + +} + +void +FlameTag::Print (std::ostream &os) const +{ + os << "receiver = " << receiver << ", transmitter = " << transmitter; +} + +//----------------------------------------------------------------------------- +// FlameProtocol +//----------------------------------------------------------------------------- +TypeId +FlameProtocol::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::flame::FlameProtocol") + .SetParent () + .AddConstructor () + .AddAttribute ( "BroadcastInterval", + "How often we must send broadcast packets", + TimeValue (Seconds (5)), + MakeTimeAccessor ( + &FlameProtocol::m_broadcastInterval), + MakeTimeChecker () + ) + .AddAttribute ( "MaxCost", + "Cost threshold after which packet will be dropeed", + UintegerValue (32), + MakeUintegerAccessor ( + &FlameProtocol::m_maxCost), + MakeUintegerChecker (3) + ) + ; + return tid; +} +FlameProtocol::FlameProtocol () : + m_address (Mac48Address ()), m_broadcastInterval (Seconds (5)), m_lastBroadcast (Simulator::Now ()), + m_maxCost (32), m_myLastSeqno (1), m_rtable (CreateObject ()) +{ +} +FlameProtocol::~FlameProtocol () +{ +} +void +FlameProtocol::DoDispose () +{ +} +bool +FlameProtocol::RequestRoute (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, + Ptr const_packet, uint16_t protocolType, RouteReplyCallback routeReply) +{ + Ptr packet = const_packet->Copy (); + if (sourceIface == m_mp->GetIfIndex ()) + { + //Packet from upper layer! + FlameTag tag; + if (packet->PeekPacketTag (tag)) + { + NS_FATAL_ERROR ("FLAME tag is not supposed to be received from upper layers"); + } + FlameRtable::LookupResult result = m_rtable->Lookup (destination); + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + m_lastBroadcast = Simulator::Now (); + } + if (m_lastBroadcast + m_broadcastInterval < Simulator::Now ()) + { + result.retransmitter = Mac48Address::GetBroadcast (); + result.ifIndex = FlameRtable::INTERFACE_ANY; + m_lastBroadcast = Simulator::Now (); + } + FlameHeader flameHdr; + flameHdr.AddCost (0); + flameHdr.SetSeqno (m_myLastSeqno++); + flameHdr.SetProtocol (protocolType); + flameHdr.SetOrigDst (destination); + flameHdr.SetOrigSrc (source); + m_stats.txBytes += packet->GetSize (); + packet->AddHeader (flameHdr); + tag.receiver = result.retransmitter; + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + m_stats.txBroadcast++; + } + else + { + m_stats.txUnicast++; + } + NS_LOG_DEBUG ("Source: send packet with RA = " << tag.receiver); + packet->AddPacketTag (tag); + routeReply (true, packet, source, destination, FLAME_PROTOCOL, result.ifIndex); + } + else + { + FlameHeader flameHdr; + packet->RemoveHeader (flameHdr); + FlameTag tag; + + if (!packet->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("FLAME tag must exist here"); + } + if (destination == Mac48Address::GetBroadcast ()) + { + //Broadcast always is forwarded as broadcast! + NS_ASSERT (HandleDataFrame (flameHdr.GetSeqno (), source, flameHdr, tag.transmitter, sourceIface)); + FlameTag tag (Mac48Address::GetBroadcast ()); + flameHdr.AddCost (1); + m_stats.txBytes += packet->GetSize (); + packet->AddHeader (flameHdr); + packet->AddPacketTag (tag); + routeReply (true, packet, source, destination, FLAME_PROTOCOL, FlameRtable::INTERFACE_ANY); + m_stats.txBroadcast++; + return true; + } + else + { + // We check sequence only when forward unicast, because broadcast-checks were done + // inside remove routing stuff. + if (HandleDataFrame (flameHdr.GetSeqno (), source, flameHdr, tag.transmitter, sourceIface)) + { + return false; + } + FlameRtable::LookupResult result = m_rtable->Lookup (destination); + if (tag.receiver != Mac48Address::GetBroadcast ()) + { + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + NS_LOG_DEBUG ("unicast packet dropped, because no route! I am " << GetAddress () + << ", RA = " << tag.receiver << ", TA = " << tag.transmitter); + m_stats.totalDropped++; + return false; + } + } + tag.receiver = result.retransmitter; + if (result.retransmitter == Mac48Address::GetBroadcast ()) + { + m_stats.txBroadcast++; + } + else + { + m_stats.txUnicast++; + } + m_stats.txBytes += packet->GetSize (); + flameHdr.AddCost (1); + packet->AddHeader (flameHdr); + packet->AddPacketTag (tag); + routeReply (true, packet, source, destination, FLAME_PROTOCOL, result.ifIndex); + return true; + } + return true; + } + return false; +} +bool +FlameProtocol::RemoveRoutingStuff (uint32_t fromIface, const Mac48Address source, + const Mac48Address destination, Ptr packet, uint16_t& protocolType) +{ + //Filter seqno: + if (source == GetAddress ()) + { + NS_LOG_DEBUG ("Dropped my own frame!"); + return false; + } + FlameTag tag; + if (!packet->RemovePacketTag (tag)) + { + NS_FATAL_ERROR ("FLAME tag must exist when packet is coming to protocol"); + } + FlameHeader flameHdr; + packet->RemoveHeader (flameHdr); + if ((destination == GetAddress ()) && (m_lastBroadcast + m_broadcastInterval < Simulator::Now ())) + { + Ptr packet = Create (); + m_mp->Send(packet, Mac48Address::GetBroadcast (), 0); + m_lastBroadcast = Simulator::Now (); + } + NS_ASSERT (protocolType == FLAME_PROTOCOL); + protocolType = flameHdr.GetProtocol (); + if ((HandleDataFrame (flameHdr.GetSeqno (), source, flameHdr, tag.transmitter, fromIface)) + || packet->GetSize () == 0) + { + return false; + } + return true; +} +bool +FlameProtocol::Install (Ptr mp) +{ + m_mp = mp; + std::vector > interfaces = mp->GetInterfaces (); + for (std::vector >::const_iterator i = interfaces.begin (); i != interfaces.end (); i++) + { + // Checking for compatible net device + Ptr wifiNetDev = (*i)->GetObject (); + if (wifiNetDev == 0) + { + return false; + } + Ptr mac = wifiNetDev->GetMac ()->GetObject (); + if (mac == 0) + { + return false; + } + // Installing plugins: + Ptr flameMac = Create (wifiNetDev->GetIfIndex (), this); + m_interfaces[wifiNetDev->GetIfIndex ()] = flameMac; + mac->SetBeaconGeneration (false); + mac->InstallPlugin (flameMac); + } + mp->SetRoutingProtocol (this); + // Mesh point aggregates all installed protocols + mp->AggregateObject (this); + m_address = Mac48Address::ConvertFrom (mp->GetAddress ());//* address; + return true; +} +Mac48Address +FlameProtocol::GetAddress () +{ + return m_address; +} +bool +FlameProtocol::HandleDataFrame (uint16_t seqno, Mac48Address source, const FlameHeader flameHdr, + Mac48Address receiver, uint32_t fromInterface) +{ + if (source == GetAddress ()) + { + m_stats.totalDropped++; + return true; + } + FlameRtable::LookupResult result = m_rtable->Lookup (source); + if ((result.retransmitter != Mac48Address::GetBroadcast ()) && ((int16_t)(result.seqnum - seqno) >= 0)) + { + return true; + } + if (flameHdr.GetCost () > m_maxCost) + { + m_stats.droppedTtl++; + return true; + } + m_rtable->AddPath (source, receiver, fromInterface, flameHdr.GetCost (), flameHdr.GetSeqno ()); + return false; +} +//Statistics: +FlameProtocol::Statistics::Statistics () : + txUnicast (0), txBroadcast (0), txBytes (0), droppedTtl (0), totalDropped (0) +{ +} +void +FlameProtocol::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; +} +void +FlameProtocol::Report (std::ostream & os) const +{ + os << "" << std::endl; + m_stats.Print (os); + for (FlamePluginMap::const_iterator plugin = m_interfaces.begin (); plugin != m_interfaces.end (); plugin++) + { + plugin->second->Report (os); + } + os << "" << std::endl; +} +void +FlameProtocol::ResetStats () +{ + m_stats = Statistics (); + for (FlamePluginMap::const_iterator plugin = m_interfaces.begin (); plugin != m_interfaces.end (); plugin++) + { + plugin->second->ResetStats (); + } +} + +} //namespace flame +} //namespace ns3 diff --git a/src/devices/mesh/flame/flame-protocol.h b/src/devices/mesh/flame/flame-protocol.h new file mode 100644 index 000000000..5344521ff --- /dev/null +++ b/src/devices/mesh/flame/flame-protocol.h @@ -0,0 +1,155 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#ifndef FLAME_PROTOCOL_H +#define FLAME_PROTOCOL_H + + +#include "ns3/mesh-l2-routing-protocol.h" +#include "ns3/nstime.h" +#include "ns3/tag.h" +#include + +/** + * \ingroup mesh + * \defgroup flame FLAME + * + * \brief Forwarding LAyer for MEshing protocol + * + * Simple L2.5 mesh routing protocol developed by + * Herman Elfrink and presented in + * "Easy Wireless: broadband ad-hoc networking for emergency services" + * by Maurits de Graaf et. al. at The Sixth Annual Mediterranean Ad Hoc + * Networking WorkShop, Corfu, Greece, June 12-15, 2007 + * + * see also Linux kernel mailing list discussion at + * http://lkml.org/lkml/2006/5/23/82 + */ +namespace ns3 { +namespace flame { +class FlameProtocolMac; +class FlameHeader; +class FlameRtable; +/** + * \ingroup flame + * \brief Transmitter and receiver addresses + */ +class FlameTag : public Tag +{ +public: + /// transmitter for incoming: + Mac48Address transmitter; + /// Receiver of the packet: + Mac48Address receiver; + + FlameTag (Mac48Address a = Mac48Address ()) : + receiver (a){} + + ///\name Inherited from Tag + //\{ + static TypeId GetTypeId (); + TypeId GetInstanceTypeId () const; + uint32_t GetSerializedSize () const; + void Serialize (TagBuffer i) const; + void Deserialize (TagBuffer i); + void Print (std::ostream &os) const; + //\} +}; + +/** + * \ingroup flame + * \brief FLAME routing protocol + */ +class FlameProtocol : public MeshL2RoutingProtocol +{ +public: + static TypeId GetTypeId (); + FlameProtocol (); + ~FlameProtocol (); + void DoDispose (); + + /// Route request, inherited from MeshL2RoutingProtocol + bool RequestRoute (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, + Ptr packet, uint16_t protocolType, RouteReplyCallback routeReply); + /// Cleanup flame headers! + bool RemoveRoutingStuff (uint32_t fromIface, const Mac48Address source, + const Mac48Address destination, Ptr packet, uint16_t& protocolType); + /** + * \brief Install FLAME on given mesh point. + * + * Installing protocol cause installing its interface MAC plugins. + * + * Also MP aggregates all installed protocols, FLAME protocol can be accessed + * via MeshPointDevice::GetObject(); + */ + bool Install (Ptr); + Mac48Address GetAddress (); + /// Statistics + void Report (std::ostream &) const; + void ResetStats (); +private: + /// LLC protocol number reserved by flame + static const uint16_t FLAME_PROTOCOL = 0x4040; + /** + * \brief Handles a packet: adds a routing information and drops packets by TTL or Seqno + * + * \return true if packet shall be dropped + */ + bool HandleDataFrame (uint16_t seqno, Mac48Address source, const FlameHeader flameHdr, Mac48Address receiver, uint32_t fromIface); + /** + * \name Information about MeshPointDeviceaddress, plugins + * \{ + */ + typedef std::map > FlamePluginMap; + FlamePluginMap m_interfaces; + Mac48Address m_address; + //\} + /** + * \name Broadcast timers: + * \{ + */ + Time m_broadcastInterval; + Time m_lastBroadcast; + //\} + /// Max Cost value (or TTL, because cost is actually hopcount) + uint8_t m_maxCost; + /// Sequence number: + uint16_t m_myLastSeqno; + /// Routing table: + Ptr m_rtable; + ///\name Statistics: + ///\{ + struct Statistics + { + uint16_t txUnicast; + uint16_t txBroadcast; + uint32_t txBytes; + uint16_t droppedTtl; + uint16_t totalDropped; + void Print (std::ostream & os) const; + Statistics (); + }; + Statistics m_stats; + ///\} + +}; +} //namespace flame +} //namespace ns3 +#endif /* FLAME_PROTOCOL_H */ diff --git a/src/devices/mesh/flame/flame-rtable.cc b/src/devices/mesh/flame/flame-rtable.cc new file mode 100644 index 000000000..c3825058d --- /dev/null +++ b/src/devices/mesh/flame/flame-rtable.cc @@ -0,0 +1,193 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ +#include "ns3/assert.h" +#include "ns3/simulator.h" +#include "ns3/test.h" +#include "ns3/log.h" + +#include "flame-rtable.h" +namespace ns3 { +namespace flame { + +NS_LOG_COMPONENT_DEFINE ("FlameRtable"); + +NS_OBJECT_ENSURE_REGISTERED (FlameRtable); + +TypeId +FlameRtable::GetTypeId () +{ + static TypeId tid = + TypeId ("ns3::flame::FlameRtable") + .SetParent () .AddConstructor () + .AddAttribute ( "Lifetime", + "The lifetime of the routing enrty", + TimeValue (Seconds (120)), MakeTimeAccessor ( + &FlameRtable::m_lifetime), + MakeTimeChecker () + ) + ; + return tid; +} +FlameRtable::FlameRtable () : + m_lifetime (Seconds (120)) +{ +} +FlameRtable::~FlameRtable () +{ +} +void +FlameRtable::DoDispose () +{ + m_routes.clear (); +} +void +FlameRtable::AddPath (const Mac48Address destination, const Mac48Address retransmitter, + const uint32_t interface, const uint8_t cost, const uint16_t seqnum) +{ + std::map::iterator i = m_routes.find (destination); + if (i == m_routes.end ()) + { + Route newroute; + newroute.cost = cost; + newroute.retransmitter = retransmitter; + newroute.interface = interface; + newroute.whenExpire = Simulator::Now () + m_lifetime; + newroute.seqnum = seqnum; + m_routes[destination] = newroute; + return; + } + i->second.seqnum = seqnum; + NS_ASSERT (i != m_routes.end ()); + i->second.retransmitter = retransmitter; + i->second.interface = interface; + i->second.cost = cost; + i->second.whenExpire = Simulator::Now () + m_lifetime; +} +FlameRtable::LookupResult +FlameRtable::Lookup (Mac48Address destination) +{ + std::map::iterator i = m_routes.find (destination); + if (i == m_routes.end ()) + { + return LookupResult (); + } + if ((i->second.whenExpire < Simulator::Now ())) + { + NS_LOG_DEBUG ("Route has expired, sorry."); + m_routes.erase (i); + return LookupResult (); + } + return LookupResult (i->second.retransmitter, i->second.interface, i->second.cost, i->second.seqnum); +} +bool +FlameRtable::LookupResult::operator== (const FlameRtable::LookupResult & o) const +{ + return (retransmitter == o.retransmitter && ifIndex == o.ifIndex && cost == o.cost && seqnum == o.seqnum); +} + +bool +FlameRtable::LookupResult::IsValid () const +{ + return !(retransmitter == Mac48Address::GetBroadcast () && ifIndex == INTERFACE_ANY && cost == MAX_COST + && seqnum == 0); +} + +#ifdef RUN_SELF_TESTS +/// Unit test for FlameRtable +class FlameRtableTest : public Test +{ +public: + FlameRtableTest (); + virtual bool + RunTests (); + +private: + /// Test Add apth and lookup path; + void + TestLookup (); + /** + * \name Test add path and try to lookup after entry has expired + * \{ + */ + void + TestAddPath (); + void + TestExpire (); + ///\} +private: + bool result; + + Mac48Address dst; + Mac48Address hop; + uint32_t iface; + uint8_t cost; + uint16_t seqnum; + Ptr table; +}; + +/// Test instance +static FlameRtableTest g_FlameRtableTest; + +FlameRtableTest::FlameRtableTest () : + Test ("Mesh/Flame/FlameRtable"), result (true), dst ("01:00:00:01:00:01"), hop ("01:00:00:01:00:03"), + iface (8010), cost (10), seqnum (1) +{ +} + +void +FlameRtableTest::TestLookup () +{ + FlameRtable::LookupResult correct (hop, iface, cost, seqnum); + + table->AddPath (dst, hop, iface, cost, seqnum); + NS_TEST_ASSERT (table->Lookup (dst) == correct); +} + +void +FlameRtableTest::TestAddPath () +{ + table->AddPath (dst, hop, iface, cost, seqnum); +} + +void +FlameRtableTest::TestExpire () +{ + // this is assumed to be called when path records are already expired + FlameRtable::LookupResult correct (hop, iface, cost, seqnum); + NS_TEST_ASSERT (!table->Lookup (dst).IsValid ()); +} +bool +FlameRtableTest::RunTests () +{ + table = CreateObject (); + + Simulator::Schedule (Seconds (0), &FlameRtableTest::TestLookup, this); + Simulator::Schedule (Seconds (1), &FlameRtableTest::TestAddPath, this); + Simulator::Schedule (Seconds (122), &FlameRtableTest::TestExpire, this); + + Simulator::Run (); + Simulator::Destroy (); + + return result; +} + +#endif // RUN_SELF_TESTS +} //namespace flame +} //namespace ns3 diff --git a/src/devices/mesh/flame/flame-rtable.h b/src/devices/mesh/flame/flame-rtable.h new file mode 100644 index 000000000..1a173255c --- /dev/null +++ b/src/devices/mesh/flame/flame-rtable.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + */ + +#ifndef FLAME_RTABLE_H +#define FLAME_RTABLE_H + +#include +#include "ns3/nstime.h" +#include "ns3/object.h" +#include "ns3/mac48-address.h" + +namespace ns3 { +namespace flame { +/** + * \ingroup flame + * + * \brief Routing table for FLAME + */ +class FlameRtable : public Object +{ +public: + /// Means all interfaces + const static uint32_t INTERFACE_ANY = 0xffffffff; + /// Maximum (the best?) path cost + const static uint32_t MAX_COST = 0xff; + + /// Route lookup result, return type of LookupXXX methods + struct LookupResult + { + Mac48Address retransmitter; + uint32_t ifIndex; + uint8_t cost; + uint16_t seqnum; + LookupResult (Mac48Address r = Mac48Address::GetBroadcast (), + uint32_t i = INTERFACE_ANY, + uint8_t c = MAX_COST, + uint16_t s = 0) + : retransmitter (r), + ifIndex (i), + cost (c), + seqnum (s) + { + } + /// True for valid route + bool IsValid () const; + /// Compare route lookup results, used by tests + bool operator==(const LookupResult & o) const; + }; +public: + static TypeId GetTypeId (); + FlameRtable (); + ~FlameRtable (); + void DoDispose (); + + /// Add path + void AddPath ( + const Mac48Address destination, + const Mac48Address retransmitter, + const uint32_t interface, + const uint8_t cost, + const uint16_t seqnum + ); + /** + * \brief Lookup path to destination + * \return Broadcast if not found + */ + LookupResult Lookup (Mac48Address destination); +private: + /// Routing table entry + struct Route + { + Mac48Address retransmitter; + uint32_t interface; + uint32_t cost; + Time whenExpire; + uint32_t seqnum; + }; + /// Lifetime parameter + Time m_lifetime; + /// List of routes + std::map m_routes; +}; + +} //namespace flame +} //namespace ns3 +#endif /* FLAME_PROTOCOL_H */ diff --git a/src/devices/mesh/flame/wscript b/src/devices/mesh/flame/wscript new file mode 100644 index 000000000..6b39918ee --- /dev/null +++ b/src/devices/mesh/flame/wscript @@ -0,0 +1,15 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + obj = bld.create_ns3_module('flame', ['mesh']) + obj.source = [ + 'flame-header.cc', + 'flame-rtable.cc', + 'flame-protocol-mac.cc', + 'flame-protocol.cc', + ] + headers = bld.new_task_gen('ns3header') + headers.module = 'flame' + headers.source = [ + 'flame-protocol.h', + ] diff --git a/src/devices/mesh/mesh-l2-routing-protocol.cc b/src/devices/mesh/mesh-l2-routing-protocol.cc new file mode 100644 index 000000000..e4f242fbf --- /dev/null +++ b/src/devices/mesh/mesh-l2-routing-protocol.cc @@ -0,0 +1,56 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#include "ns3/log.h" +#include "ns3/mesh-l2-routing-protocol.h" +#include "ns3/mesh-point-device.h" + +NS_LOG_COMPONENT_DEFINE ("MeshL2RoutingProtocol"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (MeshL2RoutingProtocol); + +TypeId +MeshL2RoutingProtocol::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MeshL2RoutingProtocol") + .SetParent (); + return tid; +} + +MeshL2RoutingProtocol::~MeshL2RoutingProtocol () +{ +} + +void +MeshL2RoutingProtocol::SetMeshPoint (Ptr mp) +{ + m_mp = mp; +} + +Ptr +MeshL2RoutingProtocol::GetMeshPoint () const +{ + return m_mp; +} + +} // namespace ns3 diff --git a/src/devices/mesh/mesh-l2-routing-protocol.h b/src/devices/mesh/mesh-l2-routing-protocol.h new file mode 100644 index 000000000..a265b3349 --- /dev/null +++ b/src/devices/mesh/mesh-l2-routing-protocol.h @@ -0,0 +1,126 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#ifndef MESH_L2_ROUTING_PROTOCOL_H +#define MESH_L2_ROUTING_PROTOCOL_H + +#include "ns3/object.h" +#include "ns3/mac48-address.h" +#include "ns3/packet.h" + +namespace ns3 { + +class Packet; +class MeshPointDevice; + +/** + * \ingroup mesh + * + * \brief Interface for L2 mesh routing protocol and mesh point communication. + * + * Every mesh routing protocol must implement this interface. Each mesh point (MeshPointDevice) is supposed + * to know single L2RoutingProtocol to work with, see MeshPointDevice::SetRoutingProtocol (). + * + * This interface is similar to ipv4 routiong protocol base class. + */ +class MeshL2RoutingProtocol : public Object +{ +public: + /// Never forget to support NS3 object model + static TypeId GetTypeId (); + /// virtual D-tor for subclasses + virtual ~MeshL2RoutingProtocol (); + /** + * Callback to be invoked when route discovery procedure is completed. + * + * \param flag indicating whether a route was actually found and all needed information is + * added to the packet succesfully + * + * \param packet for which the route was resolved. All routing information for MAC layer + * must be stored in proper tags (like in case of HWMP, when WifiMacHeader + * needs address of next hop), or must be added as a packet header (if MAC + * does not need any additional information). So, the packet is returned back + * to MeshPointDevice looks like a pure packet with ethernet header + * (i.e data + src +dst + protocol). The only special information addressed + * to MeshPointDevice is an outcoming interface ID. + * + * \param src source address of the packet + * + * \param dst destiation address of the packet + * + * \param protocol ethernet 'Protocol' field, needed to form a proper MAC-layer header + * + * \param uint32_t outcoming interface to use or 0xffffffff if packet should be sent by ALL interfaces + */ + typedef Callback, /* packet */ + Mac48Address,/* src */ + Mac48Address,/* dst */ + uint16_t, /* protocol */ + uint32_t /* out interface ID */ + > RouteReplyCallback; + /** + * Request routing information, all packets must go through this request. + * + * Note that route discovery works async. -- RequestRoute returns immediately, while + * reply callback will be called when routing information will be available. + * \return true if valid route is already known + * \param sourceIface the incoming interface of the packet + * \param source source address + * \param destination destination address + * \param packet the packet to be resolved (needed the whole packet, because + * routing information is added as tags or headers). The packet + * will be returned to reply callback. + * \param protocolType protocol ID, needed to form a proper MAC-layer header + * \param routeReply callback to be invoked after route discovery procedure, supposed + * to really send packet using routing information. + */ + virtual bool RequestRoute (uint32_t sourceIface, const Mac48Address source, const Mac48Address destination, Ptr< + const Packet> packet, uint16_t protocolType, RouteReplyCallback routeReply) = 0; + /** + * \brief When packet is ready to go to upper layer, protocol must + * remove all its information: tags, header, etc. So, + * MeshPointDevice must call this method when passing a packet to + * upper layer. + * \returns true if packet shall not be dropeed, false otherwise. + * \param fromIface the incoming interface of the packet + * \param source source address + * \param destination destination address + * \param packet the packet to be handled + * \param protocolType protocol ID, needed to form a proper MAC-layer header + * \attention protocol type is passed by reference, because may be + * changed + */ + virtual bool RemoveRoutingStuff (uint32_t fromIface, const Mac48Address source, const Mac48Address destination, Ptr< + Packet> packet, uint16_t & protocolType) = 0; + /// Set host mesh point, analog of SetNode (...) methods for upper layer protocols. + void + SetMeshPoint (Ptr mp); + /// Each mesh protocol must be installed on the mesh point to work. + Ptr + GetMeshPoint () const; +protected: + /// Host mesh point + Ptr m_mp; +}; +}//namespace ns3 +#endif diff --git a/src/devices/mesh/mesh-point-device.cc b/src/devices/mesh/mesh-point-device.cc new file mode 100644 index 000000000..5c4dba64f --- /dev/null +++ b/src/devices/mesh/mesh-point-device.cc @@ -0,0 +1,462 @@ +/* -*- 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 + * Pavel Boyko + */ + +#include "ns3/node.h" +#include "ns3/packet.h" +#include "ns3/log.h" +#include "ns3/pointer.h" +#include "ns3/mesh-point-device.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-wifi-interface-mac.h" + +NS_LOG_COMPONENT_DEFINE ("MeshPointDevice"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (MeshPointDevice); + +TypeId +MeshPointDevice::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::MeshPointDevice") + .SetParent () + .AddConstructor () + .AddAttribute ( "RoutingProtocol", + "The mesh routing protocol used by this mesh point.", + PointerValue (), + MakePointerAccessor ( + &MeshPointDevice::GetRoutingProtocol, &MeshPointDevice::SetRoutingProtocol), + MakePointerChecker< + MeshL2RoutingProtocol> ()); + return tid; +} + +MeshPointDevice::MeshPointDevice () : + m_ifIndex (0), m_mtu (1500) +{ + NS_LOG_FUNCTION_NOARGS (); + m_channel = CreateObject (); +} + +MeshPointDevice::~MeshPointDevice () +{ + NS_LOG_FUNCTION_NOARGS (); +} + +void +MeshPointDevice::DoDispose () +{ + NS_LOG_FUNCTION_NOARGS (); + for (std::vector >::iterator iter = m_ifaces.begin (); iter != m_ifaces.end (); iter++) + { + *iter = 0; + } + m_ifaces.clear (); + m_node = 0; + NetDevice::DoDispose (); + +} + +//----------------------------------------------------------------------------- +// NetDevice interface implementation +//----------------------------------------------------------------------------- + +void +MeshPointDevice::ReceiveFromDevice (Ptr incomingPort, Ptr packet, uint16_t protocol, + Address const &src, Address const &dst, PacketType packetType) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_LOG_DEBUG ("UID is " << packet->GetUid ()); + const Mac48Address src48 = Mac48Address::ConvertFrom (src); + const Mac48Address dst48 = Mac48Address::ConvertFrom (dst); + uint16_t& realProtocol = protocol; + NS_LOG_DEBUG ("SRC=" << src48 << ", DST = " << dst48 << ", I am: " << m_address); + if (!m_promiscRxCallback.IsNull ()) + { + m_promiscRxCallback (this, packet, protocol, src, dst, packetType); + } + if (dst48.IsGroup ()) + { + Ptr packet_copy = packet->Copy (); + if (m_routingProtocol->RemoveRoutingStuff (incomingPort->GetIfIndex (), src48, dst48, packet_copy, realProtocol)) + { + m_rxCallback (this, packet_copy, realProtocol, src); + Forward (incomingPort, packet, protocol, src48, dst48); + + m_rxStats.broadcastData++; + m_rxStats.broadcastDataBytes += packet->GetSize (); + } + return; + } + if (dst48 == m_address) + { + Ptr packet_copy = packet->Copy (); + if (m_routingProtocol->RemoveRoutingStuff (incomingPort->GetIfIndex (), src48, dst48, packet_copy, realProtocol)) + { + m_rxCallback (this, packet_copy, realProtocol, src); + m_rxStats.unicastData++; + m_rxStats.unicastDataBytes += packet->GetSize (); + } + return; + } + else + Forward (incomingPort, packet->Copy (), protocol, src48, dst48); +} + +void +MeshPointDevice::Forward (Ptr inport, Ptr packet, uint16_t protocol, + const Mac48Address src, const Mac48Address dst) +{ + // pass through routing protocol + m_routingProtocol->RequestRoute (inport->GetIfIndex (), src, dst, packet, protocol, MakeCallback ( + &MeshPointDevice::DoSend, this)); +} + +void +MeshPointDevice::SetIfIndex (const uint32_t index) +{ + NS_LOG_FUNCTION_NOARGS (); + m_ifIndex = index; +} + +uint32_t +MeshPointDevice::GetIfIndex () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_ifIndex; +} + +Ptr +MeshPointDevice::GetChannel () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_channel; +} + +Address +MeshPointDevice::GetAddress () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_address; +} + +void +MeshPointDevice::SetAddress (Address a) +{ + NS_LOG_WARN ("Manual changing mesh point address can cause routing errors."); + m_address = Mac48Address::ConvertFrom (a); +} + +bool +MeshPointDevice::SetMtu (const uint16_t mtu) +{ + NS_LOG_FUNCTION_NOARGS (); + m_mtu = mtu; + return true; +} + +uint16_t +MeshPointDevice::GetMtu () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_mtu; +} + +bool +MeshPointDevice::IsLinkUp () const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +void +MeshPointDevice::AddLinkChangeCallback (Callback callback) +{ + // do nothing +} + +bool +MeshPointDevice::IsBroadcast () const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +Address +MeshPointDevice::GetBroadcast () const +{ + NS_LOG_FUNCTION_NOARGS (); + return Mac48Address ("ff:ff:ff:ff:ff:ff"); +} + +bool +MeshPointDevice::IsMulticast () const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +Address +MeshPointDevice::GetMulticast (Ipv4Address multicastGroup) const +{ + NS_LOG_FUNCTION (this << multicastGroup); + Mac48Address multicast = Mac48Address::GetMulticast (multicastGroup); + return multicast; +} + +bool +MeshPointDevice::IsPointToPoint () const +{ + NS_LOG_FUNCTION_NOARGS (); + return false; +} + +bool +MeshPointDevice::IsBridge () const +{ + NS_LOG_FUNCTION_NOARGS (); + return false; +} + +bool +MeshPointDevice::Send (Ptr packet, const Address& dest, uint16_t protocolNumber) +{ + const Mac48Address dst48 = Mac48Address::ConvertFrom (dest); + return m_routingProtocol->RequestRoute (m_ifIndex, m_address, dst48, packet, protocolNumber, MakeCallback ( + &MeshPointDevice::DoSend, this)); +} + +bool +MeshPointDevice::SendFrom (Ptr packet, const Address& src, const Address& dest, + uint16_t protocolNumber) +{ + const Mac48Address src48 = Mac48Address::ConvertFrom (src); + const Mac48Address dst48 = Mac48Address::ConvertFrom (dest); + return m_routingProtocol->RequestRoute (m_ifIndex, src48, dst48, packet, protocolNumber, MakeCallback ( + &MeshPointDevice::DoSend, this)); +} + +Ptr +MeshPointDevice::GetNode () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_node; +} + +void +MeshPointDevice::SetNode (Ptr node) +{ + NS_LOG_FUNCTION_NOARGS (); + m_node = node; +} + +bool +MeshPointDevice::NeedsArp () const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +void +MeshPointDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) +{ + NS_LOG_FUNCTION_NOARGS (); + m_rxCallback = cb; +} + +void +MeshPointDevice::SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb) +{ + NS_LOG_FUNCTION_NOARGS (); + m_promiscRxCallback = cb; +} + +bool +MeshPointDevice::SupportsSendFrom () const +{ + NS_LOG_FUNCTION_NOARGS (); + return false; // don't allow to bridge mesh network with something else. +} + +Address +MeshPointDevice::GetMulticast (Ipv6Address addr) const +{ + NS_LOG_FUNCTION (this << addr); + return Mac48Address::GetMulticast (addr); +} + +//----------------------------------------------------------------------------- +// Interfaces +//----------------------------------------------------------------------------- +uint32_t +MeshPointDevice::GetNInterfaces () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_ifaces.size (); +} + +Ptr +MeshPointDevice::GetInterface (uint32_t n) const +{ + for (std::vector >::const_iterator i = m_ifaces.begin (); i != m_ifaces.end (); i++) + { + if ((*i)->GetIfIndex () == n) + { + return (*i); + } + } + NS_FATAL_ERROR ("Mesh point interface is not found by index"); + return 0; +} +std::vector > +MeshPointDevice::GetInterfaces () const +{ + return m_ifaces; +} +void +MeshPointDevice::AddInterface (Ptr iface) +{ + NS_LOG_FUNCTION_NOARGS (); + + NS_ASSERT (iface != this); + if (!Mac48Address::IsMatchingType (iface->GetAddress ())) + { + NS_FATAL_ERROR ("Device does not support eui 48 addresses: cannot be used as a mesh point interface."); + } + if (!iface->SupportsSendFrom ()) + { + NS_FATAL_ERROR ("Device does not support SendFrom: cannot be used as a mesh point interface."); + } + + // Mesh point has MAC address of it's first interface + if (m_ifaces.empty ()) + { + m_address = Mac48Address::ConvertFrom (iface->GetAddress ()); + } + Ptr wifiNetDev = iface->GetObject (); + if (wifiNetDev == 0) + { + NS_FATAL_ERROR ("Device is not a WiFi NIC: cannot be used as a mesh point interface."); + } + Ptr ifaceMac = wifiNetDev->GetMac ()->GetObject (); + if (ifaceMac == 0) + { + NS_FATAL_ERROR ( + "WiFi device doesn't have correct MAC installed: cannot be used as a mesh point interface."); + } + ifaceMac->SetMeshPointAddress (m_address); + + // Receive frames from this interface + m_node->RegisterProtocolHandler (MakeCallback (&MeshPointDevice::ReceiveFromDevice, this), 0, iface, /*promiscuous = */ + true); + m_ifaces.push_back (iface); + m_channel->AddChannel (iface->GetChannel ()); +} + +//----------------------------------------------------------------------------- +// Protocols +//----------------------------------------------------------------------------- + +void +MeshPointDevice::SetRoutingProtocol (Ptr protocol) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_ASSERT_MSG (PeekPointer (protocol->GetMeshPoint ()) == this, + "Routing protocol must be installed on mesh point to be useful."); + m_routingProtocol = protocol; +} + +Ptr +MeshPointDevice::GetRoutingProtocol () const +{ + return m_routingProtocol; +} + +void +MeshPointDevice::DoSend (bool success, Ptr packet, Mac48Address src, Mac48Address dst, + uint16_t protocol, uint32_t outIface) +{ + if (!success) + { + NS_LOG_DEBUG ("Resolve failed"); + return; + } + + // Count statistics + Statistics * stats = ((src == m_address) ? &m_txStats : &m_fwdStats); + + if (dst.IsBroadcast ()) + { + stats->broadcastData++; + stats->broadcastDataBytes += packet->GetSize (); + } + else + { + stats->unicastData++; + stats->unicastDataBytes += packet->GetSize (); + } + + // Send + if (outIface != 0xffffffff) + { + GetInterface (outIface)->SendFrom (packet, src, dst, protocol); + } + else + { + for (std::vector >::iterator i = m_ifaces.begin (); i != m_ifaces.end (); i++) + { + (*i) -> SendFrom (packet->Copy (), src, dst, protocol); + } + } +} +MeshPointDevice::Statistics::Statistics () : + unicastData (0), unicastDataBytes (0), broadcastData (0), broadcastDataBytes (0) +{ +} + +void +MeshPointDevice::Report (std::ostream & os) const +{ + os << "" << std::endl; +} + +void +MeshPointDevice::ResetStats () +{ + m_rxStats = Statistics (); + m_txStats = Statistics (); + m_fwdStats = Statistics (); +} + +} // namespace ns3 diff --git a/src/devices/mesh/mesh-point-device.h b/src/devices/mesh/mesh-point-device.h new file mode 100644 index 000000000..65aed00bb --- /dev/null +++ b/src/devices/mesh/mesh-point-device.h @@ -0,0 +1,185 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#ifndef L2ROUTING_NET_DEVICE_H +#define L2ROUTING_NET_DEVICE_H + +#include "ns3/net-device.h" +#include "ns3/mac48-address.h" +#include "ns3/bridge-channel.h" +#include "ns3/mesh-l2-routing-protocol.h" + +namespace ns3 { + +class Node; +/** + * \ingroup mesh + * + * \brief Virtual net device modeling mesh point. + * + * Mesh point is a virtual net device which is responsible for + * - Aggreagating and coordinating 1..* real devices -- mesh interfaces, see MeshInterfaceDevice class. + * - Hosting all mesh-related level 2 protocols. + * + * One of hosted L2 protocols must inplement L2RoutingProtocol interface and is used for packets forwarding. + * + * From the level 3 point of view MeshPointDevice is similar to BridgeNetDevice, but the packets, + * which going through may be changed (because L2 protocols may require their own headers or tags). + * + * Attributes: TODO + */ +class MeshPointDevice : public NetDevice +{ +public: + /// Object type ID for NS3 object system + static TypeId GetTypeId (); + /// C-tor create empty (without interfaces and protocols) mesh point + MeshPointDevice (); + /// D-tor + virtual ~MeshPointDevice (); + + ///\name Interfaces + //\{ + /** + * \brief Attach new interface to the station. Interface must support 48-bit MAC address and SendFrom method. + * + * \attention Only MeshPointDevice can have IP address, but not individual interfaces. + */ + void AddInterface (Ptr port); + /** + * \return number of interfaces + */ + uint32_t GetNInterfaces () const; + /** + * \return interface device by its index (aka ID) + * \param id is interface id, 0 <= id < GetNInterfaces + */ + Ptr GetInterface (uint32_t id) const; + /** + * \return vector of interfaces + */ + std::vector > GetInterfaces () const; + //\} + + ///\name Protocols + //\{ + /// Register routing protocol to be used. Protocol must be alredy installed on this mesh point. + void SetRoutingProtocol (Ptr protocol); + /// Access current routing protocol + Ptr GetRoutingProtocol () const; + //\} + + ///\name NetDevice interface for upper layers + //\{ + virtual void SetIfIndex (const uint32_t index); + virtual uint32_t GetIfIndex () const; + virtual Ptr GetChannel () const; + virtual Address GetAddress () const; + virtual void SetAddress (Address a); + virtual bool SetMtu (const uint16_t mtu); + virtual uint16_t GetMtu () const; + virtual bool IsLinkUp () const; + virtual void AddLinkChangeCallback (Callback callback); + virtual bool IsBroadcast () const; + virtual Address GetBroadcast () const; + virtual bool IsMulticast () const; + virtual Address GetMulticast (Ipv4Address multicastGroup) const; + virtual bool IsPointToPoint () const; + virtual bool IsBridge () const; + virtual bool Send (Ptr packet, const Address& dest, uint16_t protocolNumber); + virtual bool SendFrom (Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + virtual Ptr GetNode () const; + virtual void SetNode (Ptr node); + virtual bool NeedsArp () const; + virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb); + virtual void SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb); + virtual bool SupportsSendFrom () const; + virtual Address GetMulticast (Ipv6Address addr) const; + virtual void DoDispose (); + //\} + + ///\name Statistics + //\{ + /// Print statistics counters + void Report (std::ostream & os) const; + /// Reset statistics counters + void ResetStats (); + //\} + +private: + /// Receive packet from interface + void ReceiveFromDevice (Ptr device, Ptr packet, uint16_t protocol, + Address const &source, Address const &destination, PacketType packetType); + /// Forward packet down to interfaces + void Forward (Ptr incomingPort, Ptr packet, + uint16_t protocol, const Mac48Address src, + const Mac48Address dst); + /** + * \brief Response callback for L2 routing protocol. This will be executed when routing information is ready. + * + * \param success True is route found. TODO: diagnose routing errors + * \param packet Packet to send + * \param src Source MAC address + * \param dst Destination MAC address + * \param protocol Protocol ID + * \param iface Interface to use (ID) for send (decided by routing protocol). All interfaces will be used if outIface = 0xffffffff + */ + void + DoSend (bool success, Ptr packet, Mac48Address src, Mac48Address dst, uint16_t protocol, + uint32_t iface); + +private: + /// Receive action + NetDevice::ReceiveCallback m_rxCallback; + /// Promisc receive action + NetDevice::PromiscReceiveCallback m_promiscRxCallback; + /// Mesh point MAC address, supposed to be the address of the first added interface + Mac48Address m_address; + /// Parent node + Ptr m_node; + /// List of interfaces + std::vector< Ptr > m_ifaces; + /// If index + uint32_t m_ifIndex; + /// MTU in bytes + uint16_t m_mtu; + /// Virtual channel for upper layers + Ptr m_channel; + /// Current routing protocol, used mainly by GetRoutingProtocol + Ptr m_routingProtocol; + + ///\name Device statistics counters + ///\{ + struct Statistics + { + uint32_t unicastData; + uint32_t unicastDataBytes; + uint32_t broadcastData; + uint32_t broadcastDataBytes; + + Statistics (); + }; + /// Counters + Statistics m_rxStats, m_txStats, m_fwdStats; + ///\} +}; +} //namespace ns3 +#endif diff --git a/src/devices/mesh/mesh-wifi-beacon.cc b/src/devices/mesh/mesh-wifi-beacon.cc new file mode 100644 index 000000000..a1300e9f8 --- /dev/null +++ b/src/devices/mesh/mesh-wifi-beacon.cc @@ -0,0 +1,62 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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: Pavel Boyko + */ + +#include "ns3/mesh-wifi-beacon.h" + + +namespace ns3 { + +MeshWifiBeacon::MeshWifiBeacon (Ssid ssid, SupportedRates rates, uint64_t us) +{ + m_header.SetSsid (ssid); + m_header.SetSupportedRates (rates); + m_header.SetBeaconIntervalUs (us); +} +void +MeshWifiBeacon::AddInformationElement (Ptr ie) +{ + m_elements.AddInformationElement (ie); +} + + +Ptr +MeshWifiBeacon::CreatePacket () +{ + Ptr packet = Create (); + packet->AddHeader (m_elements); + packet->AddHeader (BeaconHeader ()); + return packet; +} +WifiMacHeader +MeshWifiBeacon::CreateHeader (Mac48Address address, Mac48Address mpAddress) +{ + WifiMacHeader hdr; + + hdr.SetBeacon (); + hdr.SetAddr1 (Mac48Address::GetBroadcast ()); + hdr.SetAddr2 (address); + hdr.SetAddr3 (mpAddress); + hdr.SetDsNotFrom (); + hdr.SetDsNotTo (); + + return hdr; +} +} // namespace ns3 + diff --git a/src/devices/mesh/mesh-wifi-beacon.h b/src/devices/mesh/mesh-wifi-beacon.h new file mode 100644 index 000000000..4ac8afed7 --- /dev/null +++ b/src/devices/mesh/mesh-wifi-beacon.h @@ -0,0 +1,76 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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: Pavel Boyko + */ + +#ifndef MESHWIFIBEACON_H_ +#define MESHWIFIBEACON_H_ + +#include "ns3/object.h" +#include "ns3/packet.h" +#include "ns3/mgt-headers.h" // from wifi module +#include "ns3/wifi-mac-header.h" +#include "ns3/wifi-information-element-vector.h" + +#include + +namespace ns3 { + +/** + * \brief Beacon is beacon header + list of arbitrary information elements + * + * It is supposed that distinct mesh protocols can use beacons to transport + * their own information elements. + */ +class MeshWifiBeacon +{ +public: + /** + * C-tor + * + * \param ssid is SSID for beacon header + * \param rates is a set of supported rates + * \param us beacon interval in microseconds + */ + MeshWifiBeacon (Ssid ssid, SupportedRates rates, uint64_t us); + /// Read standard Wifi beacon header + MgtBeaconHeader BeaconHeader () const { return m_header; } + /// Add information element + void AddInformationElement (Ptr ie); + + /** + * Create wifi header for beacon frame. + * + * \param address is sender address + * \param mpAddress is mesh point address + */ + WifiMacHeader CreateHeader (Mac48Address address, Mac48Address mpAddress); + /// Create frame = { beacon header + all information elements sorted by ElementId () } + Ptr CreatePacket (); + +private: + /// Beacon header + MgtBeaconHeader m_header; + /// List of information elements added + WifiInformationElementVector m_elements; +}; + +} + + +#endif /* MESHWIFIBEACON_H_ */ diff --git a/src/devices/mesh/mesh-wifi-interface-mac-plugin.h b/src/devices/mesh/mesh-wifi-interface-mac-plugin.h new file mode 100644 index 000000000..e064e482d --- /dev/null +++ b/src/devices/mesh/mesh-wifi-interface-mac-plugin.h @@ -0,0 +1,71 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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: Pavel Boyko + */ + +#ifndef MESHWIFIINTERFACEMACPLUGIN_H_ +#define MESHWIFIINTERFACEMACPLUGIN_H_ + +#include "ns3/wifi-mac-header.h" +#include "ns3/packet.h" +#include "ns3/mac48-address.h" +#include "ns3/mesh-wifi-beacon.h" +#include "ns3/ref-count-base.h" + +namespace ns3 { + +class MeshWifiInterfaceMac; + +/** + * \ingroup mesh + * + * \brief Common interface for mesh point interface MAC plugins + * + * TODO: plugins description + */ +class MeshWifiInterfaceMacPlugin : public RefCountBase +{ +public: + /// This is for subclasses + virtual ~MeshWifiInterfaceMacPlugin (){}; + /// Each plugin must be installed on interface to work + virtual void SetParent (Ptr parent) = 0; + /** + * \brief Process received frame + * + * \return false if (and only if) frame should be dropped + * TODO define when MAC call this + */ + virtual bool Receive (Ptr packet, const WifiMacHeader & header) = 0; + /** + * \brief Update frame before it will be forwarded down + * + * \return false if (and only if) frame should be dropped + * TODO define when MAC call this, preconditions & postconditions + */ + virtual bool UpdateOutcomingFrame (Ptr packet, WifiMacHeader & header, Mac48Address from, Mac48Address to) = 0; + /** + * \brief Update beacon before it will be formed and sent + * + * TODO define when MAC call this + */ + virtual void UpdateBeacon (MeshWifiBeacon & beacon) const = 0; +}; + +} // namespace ns3 +#endif /* MESHWIFIINTERFACEMACPLUGIN_H_ */ diff --git a/src/devices/mesh/mesh-wifi-interface-mac.cc b/src/devices/mesh/mesh-wifi-interface-mac.cc new file mode 100644 index 000000000..31020f759 --- /dev/null +++ b/src/devices/mesh/mesh-wifi-interface-mac.cc @@ -0,0 +1,742 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#include "ns3/mesh-wifi-interface-mac.h" +#include "ns3/mesh-wifi-beacon.h" +#include "ns3/log.h" +#include "ns3/wifi-phy.h" +#include "ns3/dcf-manager.h" +#include "ns3/mac-rx-middle.h" +#include "ns3/mac-low.h" +#include "ns3/dca-txop.h" +#include "ns3/random-variable.h" +#include "ns3/simulator.h" +#include "ns3/yans-wifi-phy.h" +#include "ns3/pointer.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/qos-tag.h" + +NS_LOG_COMPONENT_DEFINE ("MeshWifiInterfaceMac"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (MeshWifiInterfaceMac); + +TypeId +MeshWifiInterfaceMac::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::MeshWifiInterfaceMac") + .SetParent () + .AddConstructor () + .AddAttribute ( "BeaconInterval", + "Beacon Interval", + TimeValue (Seconds (0.5)), + + MakeTimeAccessor ( + &MeshWifiInterfaceMac::m_beaconInterval), + MakeTimeChecker () + ) + .AddAttribute ( "RandomStart", + "Window when beacon generating starts (uniform random) in seconds", + TimeValue (Seconds (0.5)), + MakeTimeAccessor ( + &MeshWifiInterfaceMac::m_randomStart), + MakeTimeChecker () + ) + .AddAttribute ( "BeaconGeneration", + "Enable/Disable Beaconing.", + BooleanValue (true), + MakeBooleanAccessor ( + &MeshWifiInterfaceMac::SetBeaconGeneration, &MeshWifiInterfaceMac::GetBeaconGeneration), + MakeBooleanChecker () + ) + .AddTraceSource ( "TxOkHeader", + "The header of successfully transmitted packet", + MakeTraceSourceAccessor ( + &MeshWifiInterfaceMac::m_txOkCallback) + ) + .AddTraceSource ( "TxErrHeader", + "The header of unsuccessfully transmitted packet", + MakeTraceSourceAccessor ( + &MeshWifiInterfaceMac::m_txErrCallback) + ) + ; + return tid; +} +MeshWifiInterfaceMac::MeshWifiInterfaceMac () +{ + NS_LOG_FUNCTION (this); + + m_rxMiddle = new MacRxMiddle (); + m_rxMiddle->SetForwardCallback (MakeCallback (&MeshWifiInterfaceMac::Receive, this)); + + m_low = CreateObject (); + m_low->SetRxCallback (MakeCallback (&MacRxMiddle::Receive, m_rxMiddle)); + + m_dcfManager = new DcfManager (); + m_dcfManager->SetupLowListener (m_low); + + m_beaconDca = CreateObject (); + m_beaconDca->SetLow (m_low); + m_beaconDca->SetMinCw (0); + m_beaconDca->SetMaxCw (0); + m_beaconDca->SetAifsn (1); + m_beaconDca->SetManager (m_dcfManager); + + SetQueue (AC_VO); + SetQueue (AC_VI); + SetQueue (AC_BE); + SetQueue (AC_BK); +} +MeshWifiInterfaceMac::~MeshWifiInterfaceMac () +{ + NS_LOG_FUNCTION (this); +} +//----------------------------------------------------------------------------- +// WifiMac inherited +//----------------------------------------------------------------------------- +void +MeshWifiInterfaceMac::SetSlot (Time slotTime) +{ + NS_LOG_FUNCTION (this << slotTime); + m_dcfManager->SetSlot (slotTime); + m_low->SetSlotTime (slotTime); + m_slot = slotTime; +} +void +MeshWifiInterfaceMac::SetSifs (Time sifs) +{ + NS_LOG_FUNCTION (this << sifs); + m_dcfManager->SetSifs (sifs); + m_low->SetSifs (sifs); + m_sifs = sifs; +} +void +MeshWifiInterfaceMac::SetAckTimeout (Time ackTimeout) +{ + m_low->SetAckTimeout (ackTimeout); +} +void +MeshWifiInterfaceMac::SetCtsTimeout (Time ctsTimeout) +{ + m_low->SetCtsTimeout (ctsTimeout); +} +void +MeshWifiInterfaceMac::SetPifs (Time pifs) +{ + NS_LOG_FUNCTION (this << pifs); + m_pifs = pifs; +} +void +MeshWifiInterfaceMac::SetEifsNoDifs (Time eifsNoDifs) +{ + NS_LOG_FUNCTION (this << eifsNoDifs); + m_dcfManager->SetEifsNoDifs (eifsNoDifs); + m_eifsNoDifs = eifsNoDifs; +} +Time +MeshWifiInterfaceMac::GetSlot () const +{ + return m_slot; +} +Time +MeshWifiInterfaceMac::GetSifs () const +{ + return m_sifs; +} +Time +MeshWifiInterfaceMac::GetEifsNoDifs () const +{ + return m_eifsNoDifs; +} +Time +MeshWifiInterfaceMac::GetAckTimeout () const +{ + return m_low->GetAckTimeout (); +} +Time +MeshWifiInterfaceMac::GetCtsTimeout () const +{ + return m_low->GetCtsTimeout (); +} +Time +MeshWifiInterfaceMac::GetPifs () const +{ + return m_low->GetPifs (); +} +void +MeshWifiInterfaceMac::SetWifiPhy (Ptr phy) +{ + NS_LOG_FUNCTION (this << phy); + m_phy = phy; + m_dcfManager->SetupPhyListener (phy); + m_low->SetPhy (phy); +} +void +MeshWifiInterfaceMac::SetWifiRemoteStationManager (Ptr stationManager) +{ + NS_LOG_FUNCTION (this << stationManager); + m_stationManager = stationManager; + for (Queues::const_iterator i = m_queues.begin (); i != m_queues.end (); i++) + { + i->second->SetWifiRemoteStationManager (stationManager); + } + m_beaconDca->SetWifiRemoteStationManager (stationManager); + m_low->SetWifiRemoteStationManager (stationManager); +} +void +MeshWifiInterfaceMac::Enqueue (Ptr packet, Mac48Address to, Mac48Address from) +{ + NS_LOG_FUNCTION (this << packet << to << from); + ForwardDown (packet, from, to); +} +void +MeshWifiInterfaceMac::Enqueue (Ptr packet, Mac48Address to) +{ + NS_LOG_FUNCTION (this << packet << to); + ForwardDown (packet, m_low->GetAddress (), to); +} +bool +MeshWifiInterfaceMac::SupportsSendFrom () const +{ + return true; +} +void +MeshWifiInterfaceMac::SetForwardUpCallback ( + Callback , Mac48Address, Mac48Address> upCallback) +{ + NS_LOG_FUNCTION (this); + m_upCallback = upCallback; +} +void +MeshWifiInterfaceMac::SetLinkUpCallback (Callback linkUp) +{ + NS_LOG_FUNCTION (this); + if (!linkUp.IsNull ()) + { + linkUp (); + } +} +void +MeshWifiInterfaceMac::SetLinkDownCallback (Callback linkDown) +{ + NS_LOG_FUNCTION (this); +} +Mac48Address +MeshWifiInterfaceMac::GetAddress () const +{ + return m_address; +} +Mac48Address +MeshWifiInterfaceMac::GetBssid () const +{ + return m_address; +} +Ssid +MeshWifiInterfaceMac::GetSsid () const +{ + return m_meshId; +} +void +MeshWifiInterfaceMac::SetAddress (Mac48Address address) +{ + NS_LOG_FUNCTION (address); + m_low->SetAddress (address); + m_address = address; +} +void +MeshWifiInterfaceMac::SetSsid (Ssid ssid) +{ + NS_LOG_FUNCTION (ssid); + m_meshId = ssid; +} +void +MeshWifiInterfaceMac::DoDispose () +{ + NS_LOG_FUNCTION (this); + delete m_rxMiddle; + delete m_dcfManager; + //Delete smart pointers: + m_rxMiddle = 0; + m_low = 0; + m_dcfManager = 0; + m_phy = 0; + m_queues.clear (); + m_beaconSendEvent.Cancel (); + m_beaconDca = 0; + + WifiMac::DoDispose (); +} +//----------------------------------------------------------------------------- +// Plugins +//----------------------------------------------------------------------------- +void +MeshWifiInterfaceMac::InstallPlugin (Ptr plugin) +{ + NS_LOG_FUNCTION (this); + + plugin->SetParent (this); + m_plugins.push_back (plugin); +} +//----------------------------------------------------------------------------- +// Switch channels +//----------------------------------------------------------------------------- +uint16_t +MeshWifiInterfaceMac::GetFrequencyChannel () const +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (m_phy != 0); // need PHY to set/get channel + + Ptr phy = m_phy->GetObject (); + if (phy != 0) + { + return phy->GetChannelNumber (); + } + else + { + return 0; + } +} +void +MeshWifiInterfaceMac::SwitchFrequencyChannel (uint16_t new_id) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (m_phy != 0); // need PHY to set/get channel + /* TODO + * + * Correct channel switching is: + * + * 1. Interface down, e.g. to stop packets from layer 3 + * 2. Wait before all output queues will be empty + * 3. Switch PHY channel + * 4. Interface up + * + * Now we use dirty channel switch -- just change frequency + */ + Ptr phy = m_phy->GetObject (); + phy->SetChannelNumber (new_id); + // Don't know NAV on new channel + m_dcfManager->NotifyNavResetNow (Seconds (0)); +} +//----------------------------------------------------------------------------- +// Forward frame up/down +//----------------------------------------------------------------------------- +void +MeshWifiInterfaceMac::ForwardUp (Ptr packet, Mac48Address src, Mac48Address dst) +{ + NS_LOG_FUNCTION (this << packet << src); + m_upCallback (packet, src, dst); +} + +void +MeshWifiInterfaceMac::ForwardDown (Ptr const_packet, Mac48Address from, Mac48Address to) +{ + // copy packet to allow modifications + Ptr packet = const_packet->Copy (); + WifiMacHeader hdr; + hdr.SetTypeData (); + hdr.SetAddr2 (GetAddress ()); + hdr.SetAddr3 (to); + hdr.SetAddr4 (from); + hdr.SetDsFrom (); + hdr.SetDsTo (); + // Fill QoS fields: + hdr.SetQosAckPolicy (WifiMacHeader::NORMAL_ACK); + hdr.SetQosNoEosp (); + hdr.SetQosNoAmsdu (); + hdr.SetQosTxopLimit (0); + // Address 1 is unknwon here. Routing plugin is responsible to correctly set it. + hdr.SetAddr1 (Mac48Address ()); + // Filter packet through all installed plugins + for (PluginList::const_iterator i = m_plugins.end () - 1; i != m_plugins.begin () - 1; i--) + { + bool drop = !((*i)->UpdateOutcomingFrame (packet, hdr, from, to)); + if (drop) + { + return; // plugin drops frame + } + } + // Assert that address1 is set. Assert will fail e.g. if there is no installed routing plugin. + NS_ASSERT (hdr.GetAddr1 () != Mac48Address ()); + // Queue frame + WifiRemoteStation *destination = m_stationManager->Lookup (to); + if (destination->IsBrandNew ()) + { + // in adhoc mode, we assume that every destination + // supports all the rates we support. + for (uint32_t i = 0; i < m_phy->GetNModes (); i++) + { + destination->AddSupportedMode (m_phy->GetMode (i)); + } + destination->RecordDisassociated (); + } + //Classify: application sets a tag, which is removed here + // Get Qos tag: + AccessClass ac = AC_BE; + QosTag tag; + if (packet->RemovePacketTag (tag)) + { + ac = QosUtilsMapTidToAc (tag.Get ()); + } + m_stats.sentFrames++; + m_stats.sentBytes += packet->GetSize (); + NS_ASSERT (m_queues.find (ac) != m_queues.end ()); + m_queues[ac]->Queue (packet, hdr); +} +void +MeshWifiInterfaceMac::SendManagementFrame (Ptr packet, const WifiMacHeader& hdr) +{ + //Filter management frames: + WifiMacHeader header = hdr; + for (PluginList::const_iterator i = m_plugins.end () - 1; i != m_plugins.begin () - 1; i--) + { + bool drop = !((*i)->UpdateOutcomingFrame (packet, header, Mac48Address (), Mac48Address ())); + if (drop) + { + return; // plugin drops frame + } + } + m_stats.sentFrames++; + m_stats.sentBytes += packet->GetSize (); + if ((m_queues.find (AC_VO) == m_queues.end ()) || (m_queues.find (AC_BK) == m_queues.end ())) + { + NS_FATAL_ERROR ("Voice or Background queue is not set up!"); + } + /* + * When we send a management frame - it is better to enqueue it to + * priority queue. But when we send a broadcast management frame, + * like PREQ, little MinCw value may cause collisions during + * retransmissions (two neighbor stations may choose the same window + * size, and two packets will be collided). So, broadcast management + * frames go to BK queue. + */ + if (hdr.GetAddr1 () != Mac48Address::GetBroadcast ()) + { + m_queues[AC_VO]->Queue (packet, header); + } + else + { + m_queues[AC_BK]->Queue (packet, header); + } +} +SupportedRates +MeshWifiInterfaceMac::GetSupportedRates () const +{ + // set the set of supported rates and make sure that we indicate + // the Basic Rate set in this set of supported rates. + SupportedRates rates; + for (uint32_t i = 0; i < m_phy->GetNModes (); i++) + { + WifiMode mode = m_phy->GetMode (i); + rates.AddSupportedRate (mode.GetDataRate ()); + } + // set the basic rates + for (uint32_t j = 0; j < m_stationManager->GetNBasicModes (); j++) + { + WifiMode mode = m_stationManager->GetBasicMode (j); + rates.SetBasicRate (mode.GetDataRate ()); + } + return rates; +} +bool +MeshWifiInterfaceMac::CheckSupportedRates (SupportedRates rates) const +{ + for (uint32_t i = 0; i < m_stationManager->GetNBasicModes (); i++) + { + WifiMode mode = m_stationManager->GetBasicMode (i); + if (!rates.IsSupportedRate (mode.GetDataRate ())) + { + return false; + } + } + return true; +} +//----------------------------------------------------------------------------- +// Beacons +//----------------------------------------------------------------------------- +void +MeshWifiInterfaceMac::SetRandomStartDelay (Time interval) +{ + NS_LOG_FUNCTION (this << interval); + m_randomStart = interval; +} +void +MeshWifiInterfaceMac::SetBeaconInterval (Time interval) +{ + NS_LOG_FUNCTION (this << interval); + m_beaconInterval = interval; +} +Time +MeshWifiInterfaceMac::GetBeaconInterval () const +{ + return m_beaconInterval; +} +void +MeshWifiInterfaceMac::SetBeaconGeneration (bool enable) +{ + NS_LOG_FUNCTION (this << enable); + UniformVariable coefficient (0.0, m_randomStart.GetSeconds ()); + if (enable) + { + Time randomStart = Seconds (coefficient.GetValue ()); + // Now start sending beacons after some random delay (to avoid collisions) + NS_ASSERT (!m_beaconSendEvent.IsRunning ()); + m_beaconSendEvent = Simulator::Schedule (randomStart, &MeshWifiInterfaceMac::SendBeacon, this); + m_tbtt = Simulator::Now () + randomStart; + } + else + { + // stop sending beacons + m_beaconSendEvent.Cancel (); + } +} +bool +MeshWifiInterfaceMac::GetBeaconGeneration () const +{ + return m_beaconSendEvent.IsRunning (); +} +Time +MeshWifiInterfaceMac::GetTbtt () const +{ + return m_tbtt; +} +void +MeshWifiInterfaceMac::ShiftTbtt (Time shift) +{ + // User of ShiftTbtt () must take care don't shift it to the past + NS_ASSERT (GetTbtt () + shift > Simulator::Now ()); + + m_tbtt += shift; + // Shift scheduled event + Simulator::Cancel (m_beaconSendEvent); + m_beaconSendEvent = Simulator::Schedule (GetTbtt () - Simulator::Now (), &MeshWifiInterfaceMac::SendBeacon, + this); +} +void +MeshWifiInterfaceMac::ScheduleNextBeacon () +{ + m_tbtt += GetBeaconInterval (); + m_beaconSendEvent = Simulator::Schedule (GetBeaconInterval (), &MeshWifiInterfaceMac::SendBeacon, this); +} +void +MeshWifiInterfaceMac::SendBeacon () +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG (GetAddress () << " is sending beacon"); + + NS_ASSERT (!m_beaconSendEvent.IsRunning ()); + NS_ASSERT (Simulator::Now ().GetMicroSeconds () == GetTbtt ().GetMicroSeconds ()); // assert that beacon is just on time + + // Form & send beacon + MeshWifiBeacon beacon (GetSsid (), GetSupportedRates (), m_beaconInterval.GetMicroSeconds ()); + + // Ask all plugins to add their specific information elements to beacon + for (PluginList::const_iterator i = m_plugins.begin (); i != m_plugins.end (); ++i) + { + (*i)->UpdateBeacon (beacon); + } + m_beaconDca->Queue (beacon.CreatePacket (), beacon.CreateHeader (GetAddress (), GetMeshPointAddress ())); + + ScheduleNextBeacon (); +} +void +MeshWifiInterfaceMac::Receive (Ptr packet, WifiMacHeader const *hdr) +{ + // Process beacon + if ((hdr->GetAddr1 () != GetAddress ()) && (hdr->GetAddr1 () != Mac48Address::GetBroadcast ())) + { + return; + } + if (hdr->IsBeacon ()) + { + m_stats.recvBeacons++; + MgtBeaconHeader beacon_hdr; + Mac48Address from = hdr->GetAddr2 (); + + packet->PeekHeader (beacon_hdr); + + NS_LOG_DEBUG ("Beacon received from " << hdr->GetAddr2 () << " I am " << GetAddress () << " at " + << Simulator::Now ().GetMicroSeconds () << " microseconds"); + + // update supported rates + if (beacon_hdr.GetSsid ().IsEqual (GetSsid ())) + { + SupportedRates rates = beacon_hdr.GetSupportedRates (); + WifiRemoteStation * peerSta = m_stationManager->Lookup (hdr->GetAddr2 ()); + + for (uint32_t i = 0; i < m_phy->GetNModes (); i++) + { + WifiMode mode = m_phy->GetMode (i); + if (rates.IsSupportedRate (mode.GetDataRate ())) + { + peerSta->AddSupportedMode (mode); + if (rates.IsBasicRate (mode.GetDataRate ())) + { + m_stationManager->AddBasicMode (mode); + } + } + } + } + } + else + { + m_stats.recvBytes += packet->GetSize (); + m_stats.recvFrames++; + } + // Filter frame through all installed plugins + for (PluginList::iterator i = m_plugins.begin (); i != m_plugins.end (); ++i) + { + bool drop = !((*i)->Receive (packet, *hdr)); + if (drop) + { + return; // plugin drops frame + } + } + // Check if QoS tag exists and add it: + if (hdr->IsQosData ()) + { + packet->AddPacketTag (QosTag (hdr->GetQosTid ())); + } + // Forward data up + if (hdr->IsData ()) + { + ForwardUp (packet, hdr->GetAddr4 (), hdr->GetAddr3 ()); + } +} +uint32_t +MeshWifiInterfaceMac::GetLinkMetric (Mac48Address peerAddress) +{ + uint32_t metric = 1; + if (!m_linkMetricCallback.IsNull ()) + { + metric = m_linkMetricCallback (peerAddress, this); + } + return metric; +} +void +MeshWifiInterfaceMac::SetLinkMetricCallback (Callback > cb) +{ + m_linkMetricCallback = cb; +} +Ptr +MeshWifiInterfaceMac::GetStationManager () +{ + return m_stationManager; +} +void +MeshWifiInterfaceMac::SetMeshPointAddress (Mac48Address a) +{ + m_mpAddress = a; +} +Mac48Address +MeshWifiInterfaceMac::GetMeshPointAddress () const +{ + return m_mpAddress; +} +//Statistics: +MeshWifiInterfaceMac::Statistics::Statistics () : + recvBeacons (0), sentFrames (0), sentBytes (0), recvFrames (0), recvBytes (0) +{ +} +void +MeshWifiInterfaceMac::Statistics::Print (std::ostream & os) const +{ + os << "" << std::endl; +} +void +MeshWifiInterfaceMac::Report (std::ostream & os) const +{ + os << "" << std::endl; + m_stats.Print (os); + os << "" << std::endl; +} +void +MeshWifiInterfaceMac::ResetStats () +{ + m_stats = Statistics::Statistics (); +} +void +MeshWifiInterfaceMac::SetQueue (AccessClass ac) +{ + if (m_queues.find (ac) != m_queues.end ()) + { + NS_LOG_WARN ("Queue is already set!"); + return; + } + Ptr queue = Create (); + queue->SetLow (m_low); + queue->SetManager (m_dcfManager); + queue->SetTxOkCallback (MakeCallback (&MeshWifiInterfaceMac::TxOk, this)); + queue->SetTxFailedCallback (MakeCallback (&MeshWifiInterfaceMac::TxFailed, this)); + + m_queues.insert (std::make_pair (ac, queue)); +} +void +MeshWifiInterfaceMac::TxOk (WifiMacHeader const &hdr) +{ + m_txOkCallback (hdr); +} +void +MeshWifiInterfaceMac::TxFailed (WifiMacHeader const &hdr) +{ + m_txErrCallback (hdr); +} + +void +MeshWifiInterfaceMac::FinishConfigureStandard (enum WifiPhyStandard standard) +{ + switch (standard) + { + case WIFI_PHY_STANDARD_holland: + // fall through + case WIFI_PHY_STANDARD_80211a: + // fall through + case WIFI_PHY_STANDARD_80211_10Mhz: + // fall through + case WIFI_PHY_STANDARD_80211_5Mhz: + ConfigureDcf (m_queues[AC_BK], 15, 1023, AC_BK); + ConfigureDcf (m_queues[AC_BE], 15, 1023, AC_BE); + ConfigureDcf (m_queues[AC_VI], 15, 1023, AC_VI); + ConfigureDcf (m_queues[AC_VO], 15, 1023, AC_VO); + break; + case WIFI_PHY_STANDARD_80211b: + ConfigureDcf (m_queues[AC_BK], 31, 1023, AC_BK); + ConfigureDcf (m_queues[AC_BE], 31, 1023, AC_BE); + ConfigureDcf (m_queues[AC_VI], 31, 1023, AC_VI); + ConfigureDcf (m_queues[AC_VO], 31, 1023, AC_VO); + break; + default: + NS_ASSERT (false); + break; + } +} + +} // namespace ns3 + diff --git a/src/devices/mesh/mesh-wifi-interface-mac.h b/src/devices/mesh/mesh-wifi-interface-mac.h new file mode 100644 index 000000000..99bffce06 --- /dev/null +++ b/src/devices/mesh/mesh-wifi-interface-mac.h @@ -0,0 +1,254 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#ifndef MESHWIFIINTERFACEMAC_H_ +#define MESHWIFIINTERFACEMAC_H_ + +#include +#include +#include "ns3/mac48-address.h" +#include "ns3/mgt-headers.h" +#include "ns3/callback.h" +#include "ns3/packet.h" +#include "ns3/nstime.h" +#include "ns3/wifi-remote-station-manager.h" +#include "ns3/wifi-mac.h" +#include "ns3/mesh-wifi-interface-mac-plugin.h" +#include "ns3/event-id.h" +#include "qos-utils.h" +namespace ns3 { + +class WifiMacHeader; +class DcaTxop; +class WifiPhy; +class DcfManager; +class MacRxMiddle; +class MacLow; +/** + * \ingroup mesh + * + * \brief Basic MAC of mesh point Wi-Fi interface. Its function is extendable through plugins mechanism. + * + * Now only three output queues are used: + * - beacons (PIFS and no backoff), + * - background traffic, + * - management and priority traffic. + * + */ +class MeshWifiInterfaceMac : public WifiMac +{ +public: + /// Never forget to support typeid + static TypeId GetTypeId (); + /// C-tor + MeshWifiInterfaceMac (); + /// D-tor + virtual ~MeshWifiInterfaceMac (); + + ///\name Inherited from WifiMac + //\{ + virtual void SetSlot (Time slotTime); + virtual void SetSifs (Time sifs); + virtual void SetPifs (Time pifs); + virtual void SetCtsTimeout (Time ctsTimeout); + virtual void SetAckTimeout (Time ackTimeout); + virtual void SetEifsNoDifs (Time eifsNoDifs); + virtual Time GetSlot () const; + virtual Time GetSifs () const; + virtual Time GetPifs () const; + virtual Time GetCtsTimeout () const; + virtual Time GetAckTimeout () const; + virtual Time GetEifsNoDifs () const; + virtual void SetWifiPhy (Ptr phy); + virtual void SetWifiRemoteStationManager (Ptr stationManager); + virtual void Enqueue (Ptr packet, Mac48Address to, Mac48Address from); + virtual void Enqueue (Ptr packet, Mac48Address to); + virtual bool SupportsSendFrom () const; + virtual void SetForwardUpCallback (Callback, Mac48Address, Mac48Address> upCallback); + virtual void SetLinkUpCallback (Callback linkUp); + virtual void SetLinkDownCallback (Callback linkDown); + virtual Mac48Address GetAddress () const; + virtual Mac48Address GetBssid () const; + virtual Ssid GetSsid () const; + virtual void SetAddress (Mac48Address address); + virtual void SetSsid (Ssid ssid); + //\} + + ///\name Each mesh point interfaces must know the mesh point address + //\{ + void SetMeshPointAddress (Mac48Address); + Mac48Address GetMeshPointAddress () const; + //\} + ///\name Beacons + //\{ + /// Set maximum initial random delay before first beacon + void SetRandomStartDelay (Time interval); + /// Set interval between two successive beacons + void SetBeaconInterval (Time interval); + /// \return interval between two beacons + Time GetBeaconInterval () const; + /** + * \brief Next beacon frame time + * + * This is supposed to be used by any entity managing beacon collision avoidance (e.g. Peer management protocol in 802.11s) + */ + Time GetTbtt () const; + /** + * \brief Shift TBTT. + * + * This is supposed to be used by any entity managing beacon collision avoidance (e.g. Peer management protocol in 802.11s) + * + * \attention User of ShiftTbtt () must take care to not shift it to the past. + */ + void ShiftTbtt (Time shift); + //\} + + ///\name Plugins + //\{ + /// Install plugin. TODO return unique ID to allow unregister plugins + void InstallPlugin (Ptr plugin); + //\} + + /** \name Channel switching + * + * Channel center frequency = Channel starting frequency + 5 * channel_id (MHz), + * where Starting channel frequency is standard-dependent as defined in IEEE 802.11-2007 17.3.8.3.2. + * + * Number of channels to use must be limited elsewhere. + */ + //\{ + /// Current channel Id + uint16_t GetFrequencyChannel () const; + /// Switch channel + void SwitchFrequencyChannel (uint16_t new_id); + //\} + + /// To be used by plugins sending management frames. + void SendManagementFrame (Ptr frame, const WifiMacHeader& hdr); + /// \return true if rates are supported + bool CheckSupportedRates (SupportedRates rates) const; + /// \return list of supported bitrates + SupportedRates GetSupportedRates () const; + ///\ name Metric Calculation routines: + ///\{ + void SetLinkMetricCallback (Callback > cb); + uint32_t GetLinkMetric (Mac48Address peerAddress); + Ptr GetStationManager (); + ///\} + ///\brief Statistics: + void Report (std::ostream &) const; + void ResetStats (); + /// Enable/disable beacons + void SetBeaconGeneration (bool enable); + void SetQueue (AccessClass ac); + virtual void FinishConfigureStandard (enum WifiPhyStandard standard); +private: + /// Frame receive handler + void Receive (Ptr packet, WifiMacHeader const *hdr); + /// Forward frame to mesh point + virtual void ForwardUp (Ptr packet, Mac48Address src, Mac48Address dst); + /// Send frame. Frame is supposed to be tagged by routing information. TODO: clarify this point + void ForwardDown (Ptr packet, Mac48Address from, Mac48Address to); + // Notify about tx OK/Error frames: + void TxOk (WifiMacHeader const &hdr); + void TxFailed (WifiMacHeader const &hdr); + /// Send beacon + void SendBeacon (); + /// Schedule next beacon + void ScheduleNextBeacon (); + /// Get current beaconing status + bool GetBeaconGeneration () const; + /// Real d-tor + virtual void DoDispose (); + +private: + typedef std::map > Queues; + typedef std::vector > PluginList; + ///\name Wifi MAC internals + //\{ + Queues m_queues; + Ptr m_beaconDca; + Ptr m_stationManager; + Ptr m_phy; + Callback , Mac48Address, Mac48Address> m_upCallback; + //\} + + ///\name Wifi timing intervals + //\{ + Time m_slot; + Time m_sifs; + Time m_pifs; + Time m_ackTimeout; + Time m_ctsTimeout; + Time m_eifsNoDifs; + //\} + + ///\name Mesh timing intervals + //\{ + /// Beaconing interval. + Time m_beaconInterval; + /// Maximum delay before first beacon + Time m_randomStart; + /// Time for the next frame + Time m_tbtt; + //\} + + /// DCF implementation + DcfManager* m_dcfManager; + /// Middle MAC sublayer + MacRxMiddle* m_rxMiddle; + /// Low MAC sublayer + Ptr m_low; + /// My address + Mac48Address m_address; + /// Mesh point address + Mac48Address m_mpAddress; + /// SSID + Ssid m_meshId; + + /// "Timer" for the next beacon + EventId m_beaconSendEvent; + /// List of all installed plugins + PluginList m_plugins; + Callback > m_linkMetricCallback; + ///\name Statistics: + ///\{ + struct Statistics + { + uint16_t recvBeacons; + uint32_t sentFrames; + uint32_t sentBytes; + uint32_t recvFrames; + uint32_t recvBytes; + void + Print (std::ostream & os) const; + Statistics (); + }; + Statistics m_stats; + ///\} + TracedCallback m_txOkCallback; + TracedCallback m_txErrCallback; + +}; + +} // namespace ns3 + +#endif /* MESHWIFIINTERFACEMAC_H_ */ diff --git a/src/devices/mesh/mesh.h b/src/devices/mesh/mesh.h new file mode 100644 index 000000000..148e4926b --- /dev/null +++ b/src/devices/mesh/mesh.h @@ -0,0 +1,104 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Aleksander Safonov + * Pavel Boyko + * + * This is top level mesh module description + */ + +/** + * \ingroup devices + * \defgroup mesh Mesh + * + * \brief MAC-layer mobile mesh networking. + * \section MeshOverview Overview of Layer-2 Mesh networking protocols + * + * The main goal of this module is to provide MAC-layer routing functionality. + + * The main part of MAC-layer routing model is specific type of a network device -- + * ns3::MeshPointDevice. Being an interface to upper-layer protocols, it provides routing functionality + * hidden from upper-layer protocols, by means of ns3::MeshL2RoutingProtocol. + * + * Our model supports stations with multiple network devices handled by a single + * MAC-layer routing protocol. So, ns3::MeshPointDevice serves as an umbrella to multiple + * network devices ("interfaces") working under the same MAC-layer routing protocol. + * + * Network devices may be of different types, each with a specific medium access method. + * So ns3::MeshL2RoutingProtocol consists of two parts: the one independent from the network device type, + * which we refer to as a routing protocol, and the other one depended on the network device type which + * we refer to as a plug-in to the routing protocol. + * + * One can imagine a MAC-layer routing as a two-tier model. ns3::MeshL2RoutingProtocol and ns3::MeshPointDevice + * belong to the upper tier. The task of ns3::MeshPointDevice is to send, receive, and forward frames, + * while the task of ns3::MeshL2RoutingProtocol is to resolve routes and keep frames waiting for route resolution. + * This functionality is independent from the types of underlying network devices ("interfaces"). + * + * The lower tier implements the part of MAC-layer routing, specific for underlying network devices + * and their medium access control methods. For example, HWMP routing protocol in IEEE802.11s + * uses its own specific management frames. + * + * At present, two routing protocols are implemented in this module: + * - HWMP (default routing protocol for IEEE802.11s standard) + Peer management protocol + * (also described in 802.11s standard draft) which is required by HWMP to manage peer links + * (it works like association mechanism in IEEE802.11). + * - FLAME (Forwarding LAyer for MEshing). + + * While HWMP only works with 802.11-MAC, FLAME works with all types of network devices, which support + * 48-bit MAC-addressing scheme. + * + * \subsection Architecture Architecture of MAC-layer routing stack + * As already mentioned, MAC-layer routing consists of two tiers. + * An ns3::MeshPointDevice which forwards frames by using an attached ns3::MeshL2RoutingProtocol forms + * the upper tier. The interface between ns3::MeshPointDevice and the upper-layer protocols is inherited + * from ns3::NetDevice class. The ns3::MeshPointDevice interacts with ns3::MeshL2RoutingProtocol as follows: + * ns3::MeshPointDevice gives to ns3::MeshL2RoutingProtocol a frame with the source and destination addresses, + * the network device index which the frame is received from, and a callback to be executed when the route is found. + * The callback is needed because all routing queues are implemented inside ns3::MeshL2RoutingProtocol. + * When the route is resolved, ns3::MeshL2RoutingProtocol returns the frame back to ns3::MeshPointDevice with the + * network device index which the packet shall be sent to. All additional routing information is stored inside + * the frame by means of tags. In the end, when all these routines are done, the frame goes to the lower tier. + + * The lower tier is responsible for filling MAC-specific headers. At present, we have only implemented the + * lower tier which is specific for ns3::WifiNetDevice. This tier is implemented as two base classes: + * ns3::MeshWifiInterfaceMac and ns3::MeshWifiInterfaceMacPlugin. The former is a new kind of WifiMac. If beacon + * generation is enabled or disabled, it implements IEEE802.11s mesh functionality or a simple ad hoc functionality + * of the MAC-high part of the WiFi model, respectively. The latter is a plug-in to L2Routing protocol. + * It handles all outgoing and incoming frames, fills headers and make decisions to drop a frame or not. Also, it + * adds information elements to beacons specific to given L2Routing protocol, if needed. + * \image html MeshArchitecture.png "Overview of the Mesh MAC-layer routing system" + * + * \subsection NewProtocol Adding a new protocol + * This module requires all the network devices operate with ns3::Mac48Address addressing scheme. + * + * To add a new L2Routing protocol, one needs to define the following: + * - Write an upper part of the protocol inherited from ns3::MeshL2Routing. + * - If the protocol works only with 802.11 MAC -- write a plug-in inherited from ns3::MeshWifiInterfaceMacPlugin + * - If the protocol works with other types of network devices -- write your own plug-in (interface for + * communication with other types of network devices is not implemented). + * + * When you implement a L2Routing protocol, remember that when you are at L2Routing tier, + * you work with a frame without an LLC header, and when you are at plug-in tier using + * ns3::MeshWifiInterfaceMacPlugin, an LLC header is already attached (by WifiNetDevice) + * + * \attention Note, when you use ns3::MeshWifiInterfaceMac, multiple plug-ins may be installed. + * + * \subsection Statistics + * Each L2Routing protocol has a structure to capture statistics, Report and ResetStats methods. + * This gives an opportunity to collect statistic to an *.xml file periodically. + */ diff --git a/src/devices/mesh/waf b/src/devices/mesh/waf new file mode 100755 index 000000000..4283ec141 --- /dev/null +++ b/src/devices/mesh/waf @@ -0,0 +1 @@ +exec "`dirname "$0"`"/../../../waf "$@" diff --git a/src/devices/mesh/wifi-information-element-vector.cc b/src/devices/mesh/wifi-information-element-vector.cc new file mode 100644 index 000000000..99d967315 --- /dev/null +++ b/src/devices/mesh/wifi-information-element-vector.cc @@ -0,0 +1,484 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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: Pavel Boyko + */ + +#include "wifi-information-element-vector.h" +#include "ns3/packet.h" +#include +#include "ns3/test.h" +#include "ns3/hwmp-protocol.h" +// All information elements: +#include "dot11s/ie-dot11s-beacon-timing.h" +#include "dot11s/ie-dot11s-configuration.h" +#include "dot11s/ie-dot11s-id.h" +#include "dot11s/ie-dot11s-metric-report.h" +#include "dot11s/ie-dot11s-peer-management.h" +#include "dot11s/ie-dot11s-peering-protocol.h" +#include "dot11s/ie-dot11s-perr.h" +#include "dot11s/ie-dot11s-prep.h" +#include "dot11s/ie-dot11s-preq.h" +#include "dot11s/ie-dot11s-rann.h" + +namespace ns3 { +bool +operator< (WifiInformationElement const & a, WifiInformationElement const & b) +{ + return (a.ElementId () < b.ElementId ()); +} + +WifiInformationElementVector::WifiInformationElementVector () : + m_maxSize (1500) +{ +} +WifiInformationElementVector::~WifiInformationElementVector () +{ +} +TypeId +WifiInformationElementVector::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::WifiInformationElementVector") + .SetParent
    (); + return tid; +} +TypeId +WifiInformationElementVector::GetInstanceTypeId () const +{ + return GetTypeId (); +} +uint32_t +WifiInformationElementVector::GetSerializedSize () const +{ + return GetSize (); +} +void +WifiInformationElementVector::Serialize (Buffer::Iterator start) const +{ + for(std::vector >::const_iterator i = m_elements.begin (); i != m_elements.end (); i ++) + { + start.WriteU8((*i)->ElementId ()); + start.WriteU8 ((*i)->GetInformationSize ()); + (*i)->SerializeInformation (start); + start.Next ((*i)->GetInformationSize ()); + } +} +uint32_t +WifiInformationElementVector::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + uint32_t size = start.GetSize(); + while (size > 0) + { + uint32_t deserialized = DeserializeSingleIe(i); + i.Next (deserialized); + size -= deserialized; + } + return i.GetDistanceFrom(start); +} +uint32_t +WifiInformationElementVector::DeserializeSingleIe(Buffer::Iterator start) +{ + Buffer::Iterator i = start; + uint8_t id = i.ReadU8 (); + uint8_t length = i.ReadU8 (); + Ptr newElement; + switch (id) + { + case IE11S_MESH_CONFIGURATION: + newElement = Create (); + break; + case IE11S_MESH_ID: + newElement = Create (); + break; + case IE11S_LINK_METRIC_REPORT: + newElement = Create (); + break; + case IE11S_PEERING_MANAGEMENT: + newElement = Create (); + break; + case IE11S_BEACON_TIMING: + newElement = Create (); + break; + case IE11S_RANN: + newElement = Create (); + break; + case IE11S_PREQ: + newElement = Create (); + break; + case IE11S_PREP: + newElement = Create (); + break; + case IE11S_PERR: + newElement = Create (); + break; + case IE11S_MESH_PEERING_PROTOCOL_VERSION: + newElement = Create (); + break; + default: + NS_FATAL_ERROR ("Information element " << (uint16_t) id << " is not implemented"); + return 0; + } + if (GetSize () + length > m_maxSize) + { + NS_FATAL_ERROR ("Check max size for information element!"); + } + newElement->DeserializeInformation (i, length); + i.Next (length); + m_elements.push_back (newElement); + return i.GetDistanceFrom(start); +} +void +WifiInformationElementVector::Print(std::ostream & os) const +{ + //TODO +} +void +WifiInformationElementVector::SetMaxSize (uint16_t size) +{ + m_maxSize = size; +} +WifiInformationElementVector::Iterator +WifiInformationElementVector::Begin () +{ + return m_elements.begin (); +} +WifiInformationElementVector::Iterator +WifiInformationElementVector::End () +{ + return m_elements.end (); +} +bool +WifiInformationElementVector::AddInformationElement (Ptr element) +{ + if (element->GetInformationSize () + 2 + GetSize () > m_maxSize) + { + return false; + } + m_elements.push_back (element); + return true; +} +Ptr +WifiInformationElementVector::FindFirst (enum WifiElementId id) const +{ + for (IE_VECTOR::const_iterator i = m_elements.begin (); i != m_elements.end (); i++) + { + if ((*i)->ElementId () == id) + { + return (*i); + } + } + return 0; +} +namespace { +struct PIEComparator +{ + bool + operator () (Ptr a, Ptr b) const + { + return ((*PeekPointer (a)) < (*PeekPointer (b))); + } +}; +} +uint32_t +WifiInformationElementVector::GetSize () const +{ + uint32_t size = 0; + for (IE_VECTOR::const_iterator i = m_elements.begin (); i != m_elements.end (); i++) + { + size += ((*i)->GetInformationSize () + 2); + } + return size; +} + +bool +operator== (const WifiInformationElementVector & a, const WifiInformationElementVector & b) +{ + if (a.m_elements.size () != b.m_elements.size ()) + { + NS_ASSERT(false); + return false; + } + WifiInformationElementVector::IE_VECTOR::const_iterator j = b.m_elements.begin (); + for (WifiInformationElementVector::IE_VECTOR::const_iterator i = a.m_elements.begin (); i + != a.m_elements.end (); i++, j++) + { + if ((*i)->ElementId () != (*j)->ElementId ()) + { + return false; + } + if ((*i)->GetInformationSize () != (*j)->GetInformationSize ()) + { + return false; + } + uint8_t id = (*i)->ElementId (); + switch (id) + { + case IE11S_MESH_CONFIGURATION: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_MESH_ID: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_LINK_METRIC_REPORT: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ( + (*j)))) + { + return false; + } + break; + case IE11S_PEERING_MANAGEMENT: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_BEACON_TIMING: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_RANN: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_PREQ: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_PREP: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + + break; + case IE11S_PERR: + if (DynamicCast ((*i)) == 0) + { + return false; + } + if (DynamicCast ((*j)) == 0) + { + return false; + } + if (!(*DynamicCast ((*i)) == *DynamicCast ((*j)))) + { + return false; + } + break; + case IE11S_MESH_PEERING_PROTOCOL_VERSION: + break; + default: + NS_FATAL_ERROR ("Information element " << (uint16_t) id << " is not implemented"); + return false; + } + } + return true; +} +#ifdef RUN_SELF_TESTS + +/// Built-in self test for WifiInformationElementVector +struct WifiInformationElementVectorBist : public Test +{ + WifiInformationElementVectorBist () : + Test ("Mesh/WifiInformationElementVector") + { + }; + virtual bool + RunTests (); +}; + +/// Test instance +static WifiInformationElementVectorBist g_IePrepBist; + +bool +WifiInformationElementVectorBist::RunTests () +{ + bool result = true; + WifiInformationElementVector vector; + { + //Mesh ID test + Ptr meshId = Create ("qwerty"); + vector.AddInformationElement (meshId); + } + { + Ptr config = Create (); + vector.AddInformationElement (config); + } + { + Ptr report = Create (123456); + vector.AddInformationElement (report); + } + { + Ptr peerMan1 = Create (); + peerMan1->SetPeerOpen (1); + Ptr peerMan2 = Create (); + peerMan2->SetPeerConfirm (1, 2); + Ptr peerMan3 = Create (); + peerMan3->SetPeerClose (1, 2, dot11s::REASON11S_MESH_CAPABILITY_POLICY_VIOLATION); + vector.AddInformationElement (peerMan1); + vector.AddInformationElement (peerMan2); + vector.AddInformationElement (peerMan3); + } + { + Ptr beaconTiming = Create (); + beaconTiming->AddNeighboursTimingElementUnit (1, Seconds (1.0), Seconds (4.0)); + beaconTiming->AddNeighboursTimingElementUnit (2, Seconds (2.0), Seconds (3.0)); + beaconTiming->AddNeighboursTimingElementUnit (3, Seconds (3.0), Seconds (2.0)); + beaconTiming->AddNeighboursTimingElementUnit (4, Seconds (4.0), Seconds (1.0)); + vector.AddInformationElement (beaconTiming); + } + { + Ptr rann = Create (); + rann->SetFlags (1); + rann->SetHopcount (2); + rann->SetTTL (4); + rann->DecrementTtl (); + NS_TEST_ASSERT_EQUAL (rann->GetTtl (), 3); + rann->SetOriginatorAddress (Mac48Address ("11:22:33:44:55:66")); + rann->SetDestSeqNumber (5); + rann->SetMetric (6); + rann->IncrementMetric (2); + NS_TEST_ASSERT_EQUAL (rann->GetMetric (), 8); + vector.AddInformationElement (rann); + } + { + Ptr preq = Create (); + preq->SetHopcount (0); + preq->SetTTL (1); + preq->SetPreqID (2); + preq->SetOriginatorAddress (Mac48Address ("11:22:33:44:55:66")); + preq->SetOriginatorSeqNumber (3); + preq->SetLifetime (4); + preq->AddDestinationAddressElement (false, false, Mac48Address ("11:11:11:11:11:11"), 5); + preq->AddDestinationAddressElement (false, false, Mac48Address ("22:22:22:22:22:22"), 6); + vector.AddInformationElement (preq); + } + { + Ptr prep = Create (); + prep->SetFlags (12); + prep->SetHopcount (11); + prep->SetTtl (10); + prep->SetDestinationAddress (Mac48Address ("11:22:33:44:55:66")); + prep->SetDestinationSeqNumber (123); + prep->SetLifetime (5000); + prep->SetMetric (4321); + prep->SetOriginatorAddress (Mac48Address ("33:00:22:00:11:00")); + prep->SetOriginatorSeqNumber (666); + vector.AddInformationElement (prep); + } + { + Ptr perr = Create (); + dot11s::HwmpProtocol::FailedDestination dest; + dest.destination = Mac48Address ("11:22:33:44:55:66"); + dest.seqnum = 1; + perr->AddAddressUnit (dest); + dest.destination = Mac48Address ("10:20:30:40:50:60"); + dest.seqnum = 2; + perr->AddAddressUnit (dest); + dest.destination = Mac48Address ("01:02:03:04:05:06"); + dest.seqnum = 3; + perr->AddAddressUnit (dest); + vector.AddInformationElement (perr); + } + Ptr packet = Create (); + packet->AddHeader (vector); + WifiInformationElementVector resultVector; + packet->RemoveHeader (resultVector); + NS_TEST_ASSERT (vector == resultVector); + + return result; +} + +#endif // RUN_SELF_TESTS +} //namespace ns3 diff --git a/src/devices/mesh/wifi-information-element-vector.h b/src/devices/mesh/wifi-information-element-vector.h new file mode 100644 index 000000000..e6506c58c --- /dev/null +++ b/src/devices/mesh/wifi-information-element-vector.h @@ -0,0 +1,157 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 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 + * + * Authors: Kirill Andreev + * Pavel Boyko + */ + +#ifndef IE_VECTOR_H +#define IE_VECTOR_H + +#include "ns3/header.h" + +namespace ns3 { +class Packet; +/** + * \ingroup mesh + * + * \brief Enum of all known information element id (aka tags). + * + * For now only 802.11s (mesh) related elements are supported here (so 11S prefix), + * but this can change in future. + * + * Note that 802.11s element ids are not yet officially assigned, we use ones + * compatible with open80211s (http://o11s.org/) implementation. + */ +enum WifiElementId { + /* begin of open80211s-compatible IDs */ + IE11S_MESH_CONFIGURATION = 51, + IE11S_MESH_ID = 52, + /* end of open80211s-compatible IDs */ + IE11S_LINK_METRIC_REPORT = 20, + IE11S_CONGESTION_NOTIFICATION, + /* begin of open80211s-compatible IDs */ + IE11S_PEERING_MANAGEMENT = 55, + /* end of open80211s-compatible IDs */ + IE11S_SUPP_MBSS_REG_CLASSES_CHANNELS = 23, + IE11S_MESH_CHANNEL_SWITCH_ANNOUNCEMENT, + IE11S_MESH_TIM, + IE11S_AWAKE_WINDOW, + IE11S_BEACON_TIMING, + IE11S_MCCAOP_SETUP_REQUEST, + IE11S_MCCAOP_SETUP_REPLY, + IE11S_MCCAOP_ADVERTISEMENT, + IE11S_MCCAOP_RESERVATION_TEARDOWN, + IE11S_PORTAL_ANNOUNCEMENT, + IE11S_RANN = 67, + /* begin of open80211s-compatible IDs */ + IE11S_PREQ = 68, + IE11S_PREP = 69, + IE11S_PERR = 70, + /* end of open80211s-compatible IDs */ + IE11S_PROXY_UPDATE = 37, + IE11S_PROXY_UPDATE_CONFIRMATION, + IE11S_ABBREVIATED_HANDSHAKE, + IE11S_MESH_PEERING_PROTOCOL_VERSION = 74, +}; + +/** + * \ingroup mesh + * + * \brief Information element, as defined in 802.11-2007 standard + * + * Elements are defined to have a common general format consisting of a 1 octet Element ID field, a 1 octet + * length field, and a variable-length element-specific information field. Each element is assigned a unique + * Element ID as defined in this standard. The Length field specifies the number of octets in the Information + * field. + */ +class WifiInformationElement : public RefCountBase +{ +public: + ///\name Each subclass must implement + //\{ + virtual void Print (std::ostream &os) const = 0; + /// Own unique Element ID + virtual WifiElementId ElementId () const = 0; + /// Length of serialized information + virtual uint8_t GetInformationSize () const = 0; + /// Serialize information + virtual void SerializeInformation (Buffer::Iterator start) const = 0; + /// Deserialize information + virtual uint8_t DeserializeInformation (Buffer::Iterator start, uint8_t length) = 0; + //\} + + /// Compare information elements using Element ID + friend bool operator< (WifiInformationElement const & a, WifiInformationElement const & b); + /// + virtual bool operator== (WifiInformationElement const & a) { return false; } +}; + +/// Compare information elements using Element ID +bool operator< (WifiInformationElement const & a, WifiInformationElement const & b); + +/** + * \ingroup mesh + * + * \brief Information element vector + * + * Implements a vector of WifiInformationElement's + */ +class WifiInformationElementVector : public Header +{ +public: + WifiInformationElementVector (); + ~WifiInformationElementVector (); + ///\name Inherited from Header + //\{ + static TypeId GetTypeId (); + TypeId GetInstanceTypeId () const; + virtual uint32_t GetSerializedSize () const; + virtual void Serialize (Buffer::Iterator start) const; + /** + * \attention When you use RemoveHeader, WifiInformationElementVector supposes, that + * all buffer consists of information elements + * @param start + * @return + */ + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + //\} + /** + * \brief Needed when you try to deserialize a lonely IE inside other header + * \param start is the start of the buffer + * \return deserialized bytes + */ + virtual uint32_t DeserializeSingleIe (Buffer::Iterator start); + ///Set maximum size to control overflow of the max packet length + void SetMaxSize (uint16_t size); + typedef std::vector >::iterator Iterator; + Iterator Begin (); + Iterator End (); + bool AddInformationElement (Ptr element); + Ptr FindFirst (enum WifiElementId id) const; +private: + typedef std::vector > IE_VECTOR; + uint32_t GetSize () const; + IE_VECTOR m_elements; + /// Size in bytes (actually, max packet length) + uint16_t m_maxSize; + friend bool operator== (const WifiInformationElementVector & a, const WifiInformationElementVector & b); +}; +bool operator== (const WifiInformationElementVector & a, const WifiInformationElementVector & b); +} +#endif diff --git a/src/devices/mesh/wscript b/src/devices/mesh/wscript new file mode 100644 index 000000000..d11c8cb1d --- /dev/null +++ b/src/devices/mesh/wscript @@ -0,0 +1,22 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def build(bld): + obj = bld.create_ns3_module('mesh', ['wifi']) + + obj.source = [ + 'wifi-information-element-vector.cc', + 'mesh-point-device.cc', + 'mesh-l2-routing-protocol.cc', + 'mesh-wifi-beacon.cc', + 'mesh-wifi-interface-mac.cc', + ] + headers = bld.new_task_gen('ns3header') + headers.module = 'mesh' + headers.source = [ + 'wifi-information-element-vector.h', + 'mesh-point-device.h', + 'mesh-l2-routing-protocol.h', + 'mesh-wifi-beacon.h', + 'mesh-wifi-interface-mac.h', + 'mesh-wifi-interface-mac-plugin.h', + ] diff --git a/src/devices/wifi/wscript b/src/devices/wifi/wscript index 7df865e15..afdcb7776 100644 --- a/src/devices/wifi/wscript +++ b/src/devices/wifi/wscript @@ -1,12 +1,5 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- -def configure(conf): - have_gsl = conf.pkg_check_modules('GSL', 'gsl', mandatory=False) - conf.env['ENABLE_GSL'] = have_gsl - conf.report_optional_feature("GSL", "GNU Scientific Library (GSL)", - conf.env['ENABLE_GSL'], - "GSL not found") - def build(bld): obj = bld.create_ns3_module('wifi', ['node']) obj.source = [ @@ -120,7 +113,6 @@ def build(bld): if bld.env['ENABLE_GSL']: obj.uselib = 'GSL GSLCBLAS M' - obj.env.append_value('CXXDEFINES', "ENABLE_GSL") obj = bld.create_ns3_program('wifi-phy-test', ['core', 'simulator', 'mobility', 'node', 'wifi']) diff --git a/src/helper/dot11s-installer.cc b/src/helper/dot11s-installer.cc new file mode 100644 index 000000000..9ed804b0d --- /dev/null +++ b/src/helper/dot11s-installer.cc @@ -0,0 +1,124 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ +#include "ns3/dot11s-installer.h" +#include "ns3/peer-management-protocol.h" +#include "ns3/hwmp-protocol.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-wifi-interface-mac.h" + +namespace ns3 { +using namespace dot11s; +NS_OBJECT_ENSURE_REGISTERED (Dot11sStack); +TypeId +Dot11sStack::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Dot11sStack") + .SetParent () + .AddConstructor () + .AddAttribute ("Root", + "The MAC address of root mesh point.", + Mac48AddressValue (Mac48Address ("ff:ff:ff:ff:ff:ff")), + MakeMac48AddressAccessor (&Dot11sStack::m_root), + MakeMac48AddressChecker ()); + return tid; +} +Dot11sStack::Dot11sStack () : + m_root (Mac48Address ("ff:ff:ff:ff:ff:ff")) +{ +} +Dot11sStack::~Dot11sStack () +{ +} +void +Dot11sStack::DoDispose () +{ +} +bool +Dot11sStack::InstallStack (Ptr mp) +{ + //Install Peer management protocol: + Ptr pmp = CreateObject (); + pmp->SetMeshId ("mesh"); + bool install_ok = pmp->Install (mp); + if (!install_ok) + { + return false; + } + //Install HWMP: + Ptr hwmp = CreateObject (); + install_ok = hwmp->Install (mp); + if (!install_ok) + { + return false; + } + if (mp->GetAddress() == m_root) + { + hwmp->SetRoot (); + } + //Install interaction between HWMP and Peer management protocol: + pmp->SetPeerLinkStatusCallback (MakeCallback (&HwmpProtocol::PeerLinkStatus, hwmp)); + hwmp->SetNeighboursCallback (MakeCallback (&PeerManagementProtocol::GetActiveLinks, pmp)); + return true; +} +void +Dot11sStack::Report (const Ptr mp, std::ostream& os) +{ + mp->Report (os); + + std::vector > ifaces = mp->GetInterfaces (); + for (std::vector >::const_iterator i = ifaces.begin (); i != ifaces.end (); ++i) + { + Ptr device = (*i)->GetObject (); + NS_ASSERT (device != 0); + Ptr mac = device->GetMac ()->GetObject (); + NS_ASSERT (mac != 0); + mac->Report (os); + } + Ptr hwmp = mp->GetObject (); + NS_ASSERT (hwmp != 0); + hwmp->Report (os); + + Ptr pmp = mp->GetObject (); + NS_ASSERT (pmp != 0); + pmp->Report (os); +} +void +Dot11sStack::ResetStats (const Ptr mp) +{ + mp->ResetStats (); + + std::vector > ifaces = mp->GetInterfaces (); + for (std::vector >::const_iterator i = ifaces.begin (); i != ifaces.end (); ++i) + { + Ptr device = (*i)->GetObject (); + NS_ASSERT (device != 0); + Ptr mac = device->GetMac ()->GetObject (); + NS_ASSERT (mac != 0); + mac->ResetStats (); + } + Ptr hwmp = mp->GetObject (); + NS_ASSERT (hwmp != 0); + hwmp->ResetStats (); + + Ptr pmp = mp->GetObject (); + NS_ASSERT (pmp != 0); + pmp->ResetStats (); +} +} //namespace ns3 diff --git a/src/helper/dot11s-installer.h b/src/helper/dot11s-installer.h new file mode 100644 index 000000000..e2139e09b --- /dev/null +++ b/src/helper/dot11s-installer.h @@ -0,0 +1,43 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + + +#ifndef DOT11S_STACK_INSTALLER_H +#define DOT11S_STACK_INSTALLER_H +#include "ns3/mesh-stack-installer.h" +namespace ns3 { +class Dot11sStack : public MeshStack +{ + public: + static TypeId GetTypeId (); + Dot11sStack (); + ~Dot11sStack (); + void DoDispose (); + + ///\brief Installs 802.11s stack. needed by helper only + bool InstallStack (Ptr mp); + void Report (const Ptr mp, std::ostream&); + void ResetStats (const Ptr mp); + private: + Mac48Address m_root; +}; +} //namespace ns3 +#endif + diff --git a/src/helper/flame-installer.cc b/src/helper/flame-installer.cc new file mode 100644 index 000000000..c7f09f97d --- /dev/null +++ b/src/helper/flame-installer.cc @@ -0,0 +1,73 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#include "ns3/flame-installer.h" +#include "ns3/flame-protocol.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-wifi-interface-mac.h" + +namespace ns3 +{ +using namespace flame; +NS_OBJECT_ENSURE_REGISTERED (FlameStack); +TypeId +FlameStack::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::FlameStack") + .SetParent () + .AddConstructor (); + return tid; +} +FlameStack::FlameStack () +{ +} +FlameStack::~FlameStack () +{ +} +void +FlameStack::DoDispose () +{ +} +bool +FlameStack::InstallStack (Ptr mp) +{ + Ptr flame = CreateObject (); + return flame->Install (mp); +} +void +FlameStack::Report (const Ptr mp, std::ostream& os) +{ + mp->Report (os); + // TODO report flame counters + Ptr flame = mp->GetObject (); + NS_ASSERT (flame != 0); + flame->Report (os); +} +void +FlameStack::ResetStats (const Ptr mp) +{ + mp->ResetStats (); + // TODO reset flame counters + Ptr flame = mp->GetObject (); + NS_ASSERT (flame != 0); + + flame->ResetStats (); +} +} //namespace ns3 diff --git a/src/helper/flame-installer.h b/src/helper/flame-installer.h new file mode 100644 index 000000000..12b71e713 --- /dev/null +++ b/src/helper/flame-installer.h @@ -0,0 +1,45 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + +#ifndef FLAME_STACK_INSTALLER_H +#define FLAME_STACK_INSTALLER_H +#include "ns3/mesh-stack-installer.h" +namespace ns3 { +/** + * \ingroup flame + * + * \brief FLAME mesh stack (actually single protocol in this stack) + */ +class FlameStack : public MeshStack +{ + public: + static TypeId GetTypeId (); + FlameStack (); + ~FlameStack (); + void DoDispose (); + + /// Installs flame stack on given mesh point device. + bool InstallStack (Ptr mp); + void Report (const Ptr mp, std::ostream&); + void ResetStats (const Ptr mp); +}; +} //namespace ns3 +#endif + diff --git a/src/helper/internet-stack-helper.h b/src/helper/internet-stack-helper.h index b66c1645f..2e9bcb269 100644 --- a/src/helper/internet-stack-helper.h +++ b/src/helper/internet-stack-helper.h @@ -26,6 +26,8 @@ #include "ns3/packet.h" #include "ns3/ptr.h" #include "ns3/object-factory.h" +#include "ns3/pcap-writer.h" +#include "ns3/ascii-writer.h" namespace ns3 { diff --git a/src/helper/mesh-helper.cc b/src/helper/mesh-helper.cc new file mode 100644 index 000000000..876afb00f --- /dev/null +++ b/src/helper/mesh-helper.cc @@ -0,0 +1,207 @@ +/* -*- 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 + * Pavel Boyko + */ +#include "mesh-helper.h" +#include "ns3/simulator.h" +#include "ns3/mesh-point-device.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mesh-wifi-interface-mac.h" +namespace ns3 +{ +MeshHelper::MeshHelper () : + m_nInterfaces (1), + m_spreadChannelPolicy (ZERO_CHANNEL), + m_stack (0), + m_standard (WIFI_PHY_STANDARD_80211a) +{ +} +void +MeshHelper::SetSpreadInterfaceChannels (enum ChannelPolicy policy) +{ + m_spreadChannelPolicy = policy; +} +void +MeshHelper::SetStackInstaller (std::string type, + std::string n0, const AttributeValue &v0, + std::string n1, const AttributeValue &v1, + std::string n2, const AttributeValue &v2, + std::string n3, const AttributeValue &v3, + std::string n4, const AttributeValue &v4, + std::string n5, const AttributeValue &v5, + std::string n6, const AttributeValue &v6, + std::string n7, const AttributeValue &v7) +{ + m_stackFactory.SetTypeId (type); + m_stackFactory.Set (n0, v0); + m_stackFactory.Set (n1, v1); + m_stackFactory.Set (n2, v2); + m_stackFactory.Set (n3, v3); + m_stackFactory.Set (n4, v4); + m_stackFactory.Set (n5, v5); + m_stackFactory.Set (n6, v6); + m_stackFactory.Set (n7, v7); + + m_stack = m_stackFactory.Create (); + if (m_stack == 0) + { + NS_FATAL_ERROR ("Stack has not been created: " << type); + } +} + +void +MeshHelper::SetNumberOfInterfaces (uint32_t nInterfaces) +{ + m_nInterfaces = nInterfaces; +} +NetDeviceContainer +MeshHelper::Install (const WifiPhyHelper &phyHelper, NodeContainer c) const +{ + NetDeviceContainer devices; + NS_ASSERT (m_stack != 0); + for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i) + { + Ptr node = *i; + // Create a mesh point device + Ptr mp = CreateObject (); + node->AddDevice (mp); + // Create wifi interfaces (single interface by default) + for (uint32_t i = 0; i < m_nInterfaces; ++i) + { + uint32_t channel = 0; + if (m_spreadChannelPolicy == ZERO_CHANNEL) + { + channel = 0; + } + if (m_spreadChannelPolicy == SPREAD_CHANNELS) + { + channel = i * 5; + } + Ptr iface = CreateInterface (phyHelper, node, channel); + mp->AddInterface (iface); + } + if (!m_stack->InstallStack (mp)) + { + NS_FATAL_ERROR ("Stack is not installed!"); + } + devices.Add (mp); + } + return devices; +} +MeshHelper +MeshHelper::Default (void) +{ + MeshHelper helper; + helper.SetMacType (); + helper.SetRemoteStationManager ("ns3::ArfWifiManager"); + helper.SetSpreadInterfaceChannels (SPREAD_CHANNELS); + return helper; +} + +void +MeshHelper::SetMacType (std::string n0, const AttributeValue &v0, + std::string n1, const AttributeValue &v1, + std::string n2, const AttributeValue &v2, + std::string n3, const AttributeValue &v3, + std::string n4, const AttributeValue &v4, + std::string n5, const AttributeValue &v5, + std::string n6, const AttributeValue &v6, + std::string n7, const AttributeValue &v7) +{ + m_mac.SetTypeId ("ns3::MeshWifiInterfaceMac"); + m_mac.Set (n0, v0); + m_mac.Set (n1, v1); + m_mac.Set (n2, v2); + m_mac.Set (n3, v3); + m_mac.Set (n4, v4); + m_mac.Set (n5, v5); + m_mac.Set (n6, v6); + m_mac.Set (n7, v7); +} +void +MeshHelper::SetRemoteStationManager (std::string type, + std::string n0, const AttributeValue &v0, + std::string n1, const AttributeValue &v1, + std::string n2, const AttributeValue &v2, + std::string n3, const AttributeValue &v3, + std::string n4, const AttributeValue &v4, + std::string n5, const AttributeValue &v5, + std::string n6, const AttributeValue &v6, + std::string n7, const AttributeValue &v7) +{ + m_stationManager = ObjectFactory (); + m_stationManager.SetTypeId (type); + m_stationManager.Set (n0, v0); + m_stationManager.Set (n1, v1); + m_stationManager.Set (n2, v2); + m_stationManager.Set (n3, v3); + m_stationManager.Set (n4, v4); + m_stationManager.Set (n5, v5); + m_stationManager.Set (n6, v6); + m_stationManager.Set (n7, v7); +} +void +MeshHelper::SetStandard (enum WifiPhyStandard standard) +{ + m_standard = standard; +} + +Ptr +MeshHelper::CreateInterface (const WifiPhyHelper &phyHelper, Ptr node, uint16_t channelId) const +{ + Ptr device = CreateObject (); + + Ptr mac = m_mac.Create (); + NS_ASSERT (mac != 0); + mac->SetSsid (Ssid ()); + Ptr manager = m_stationManager.Create (); + NS_ASSERT (manager != 0); + Ptr phy = phyHelper.Create (node, device); + mac->SetAddress (Mac48Address::Allocate ()); + mac->ConfigureStandard (m_standard); + phy->ConfigureStandard (m_standard); + device->SetMac (mac); + device->SetPhy (phy); + device->SetRemoteStationManager (manager); + node->AddDevice (device); + mac->SwitchFrequencyChannel (channelId); + return device; +} +void +MeshHelper::Report (const ns3::Ptr& device, std::ostream& os) +{ + NS_ASSERT (m_stack != 0); + Ptr mp = device->GetObject (); + NS_ASSERT (mp != 0); + std::vector > ifaces = mp->GetInterfaces (); + os << "GetAddress ()) << "\">\n"; + m_stack->Report (mp, os); + os << "\n"; +} +void +MeshHelper::ResetStats (const ns3::Ptr& device) +{ + NS_ASSERT (m_stack != 0); + Ptr mp = device->GetObject (); + NS_ASSERT (mp != 0); + m_stack->ResetStats (mp); +} +} //namespace ns3 + diff --git a/src/helper/mesh-helper.h b/src/helper/mesh-helper.h new file mode 100644 index 000000000..0a1d0e79a --- /dev/null +++ b/src/helper/mesh-helper.h @@ -0,0 +1,187 @@ +/* -*- 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 + * Pavel Boyko + */ + + +#ifndef MESH_HELPER_H +#define MESH_HELPER_H + +#include "ns3/wifi-helper.h" +#include "ns3/mesh-stack-installer.h" + +namespace ns3 { + +class WifiChannel; + +/** + * \ingroup dot11s + * + * \brief Helper to create IEEE 802.11s mesh networks + */ +class MeshHelper +{ +public: + MeshHelper (); + static MeshHelper + Default (); + /** + * \param n0 the name of the attribute to set + * \param v0 the value of the attribute to set + * \param n1 the name of the attribute to set + * \param v1 the value of the attribute to set + * \param n2 the name of the attribute to set + * \param v2 the value of the attribute to set + * \param n3 the name of the attribute to set + * \param v3 the value of the attribute to set + * \param n4 the name of the attribute to set + * \param v4 the value of the attribute to set + * \param n5 the name of the attribute to set + * \param v5 the value of the attribute to set + * \param n6 the name of the attribute to set + * \param v6 the value of the attribute to set + * \param n7 the name of the attribute to set + * \param v7 the value of the attribute to set + * + * All the attributes specified in this method should exist + * in the requested mac. + */ + void SetMacType (std::string n0 = "", const AttributeValue &v0 = EmptyAttributeValue (), + std::string n1 = "", const AttributeValue &v1 = EmptyAttributeValue (), + std::string n2 = "", const AttributeValue &v2 = EmptyAttributeValue (), + std::string n3 = "", const AttributeValue &v3 = EmptyAttributeValue (), + std::string n4 = "", const AttributeValue &v4 = EmptyAttributeValue (), + std::string n5 = "", const AttributeValue &v5 = EmptyAttributeValue (), + std::string n6 = "", const AttributeValue &v6 = EmptyAttributeValue (), + std::string n7 = "", const AttributeValue &v7 = EmptyAttributeValue ()); + /** + * \param type the type of ns3::WifiRemoteStationManager to create. + * \param n0 the name of the attribute to set + * \param v0 the value of the attribute to set + * \param n1 the name of the attribute to set + * \param v1 the value of the attribute to set + * \param n2 the name of the attribute to set + * \param v2 the value of the attribute to set + * \param n3 the name of the attribute to set + * \param v3 the value of the attribute to set + * \param n4 the name of the attribute to set + * \param v4 the value of the attribute to set + * \param n5 the name of the attribute to set + * \param v5 the value of the attribute to set + * \param n6 the name of the attribute to set + * \param v6 the value of the attribute to set + * \param n7 the name of the attribute to set + * \param v7 the value of the attribute to set + * + * All the attributes specified in this method should exist + * in the requested station manager. + */ + void + SetRemoteStationManager (std::string type, + std::string n0 = "", const AttributeValue &v0 = EmptyAttributeValue (), + std::string n1 = "", const AttributeValue &v1 = EmptyAttributeValue (), + std::string n2 = "", const AttributeValue &v2 = EmptyAttributeValue (), + std::string n3 = "", const AttributeValue &v3 = EmptyAttributeValue (), + std::string n4 = "", const AttributeValue &v4 = EmptyAttributeValue (), + std::string n5 = "", const AttributeValue &v5 = EmptyAttributeValue (), + std::string n6 = "", const AttributeValue &v6 = EmptyAttributeValue (), + std::string n7 = "", const AttributeValue &v7 = EmptyAttributeValue ()); + /** + * Set PHY standard + */ + void SetStandard (enum WifiPhyStandard standard); + void SetMeshId (std::string s); + /** + * \brief Spread/not spread frequency channels of MP interfaces. + * + * If set to true different non-overlaping 20MHz frequency + * channels will be assigned to different mesh point interfaces. + */ + enum ChannelPolicy + { + SPREAD_CHANNELS, + ZERO_CHANNEL + }; + void SetSpreadInterfaceChannels (ChannelPolicy); + /** + * \brief Set a number of interfaces in a mesh network + * \param nInterfaces is the number of interfaces + */ + void SetNumberOfInterfaces (uint32_t nInterfaces); + + /** + * \brief Install 802.11s mesh device & protocols on given node list + * + * \param phyHelper Wifi PHY helper + * \param c List of nodes to install + * + * \return list of created mesh point devices, see MeshPointDevice + */ + NetDeviceContainer + Install (const WifiPhyHelper &phyHelper, NodeContainer c) const; + /** + * \param type the type of ns3::MeshStack. + * \param n0 the name of the attribute to set + * \param v0 the value of the attribute to set + * \param n1 the name of the attribute to set + * \param v1 the value of the attribute to set + * \param n2 the name of the attribute to set + * \param v2 the value of the attribute to set + * \param n3 the name of the attribute to set + * \param v3 the value of the attribute to set + * \param n4 the name of the attribute to set + * \param v4 the value of the attribute to set + * \param n5 the name of the attribute to set + * \param v5 the value of the attribute to set + * \param n6 the name of the attribute to set + * \param v6 the value of the attribute to set + * \param n7 the name of the attribute to set + * \param v7 the value of the attribute to set + */ + void SetStackInstaller (std::string type, + std::string n0 = "", const AttributeValue &v0 = EmptyAttributeValue (), + std::string n1 = "", const AttributeValue &v1 = EmptyAttributeValue (), + std::string n2 = "", const AttributeValue &v2 = EmptyAttributeValue (), + std::string n3 = "", const AttributeValue &v3 = EmptyAttributeValue (), + std::string n4 = "", const AttributeValue &v4 = EmptyAttributeValue (), + std::string n5 = "", const AttributeValue &v5 = EmptyAttributeValue (), + std::string n6 = "", const AttributeValue &v6 = EmptyAttributeValue (), + std::string n7 = "", const AttributeValue &v7 = EmptyAttributeValue ()); + void Report (const ns3::Ptr&, std::ostream&); + void ResetStats (const ns3::Ptr&); +private: + /** + * \returns a WifiNetDevice with ready-to-use interface + */ + Ptr CreateInterface (const WifiPhyHelper &phyHelper, Ptr node, uint16_t channelId) const; + uint32_t m_nInterfaces; + ChannelPolicy m_spreadChannelPolicy; + Ptr m_stack; + ObjectFactory m_stackFactory; + ///\name Interface factory + ///\{ + ObjectFactory m_mac; + ObjectFactory m_stationManager; + enum WifiPhyStandard m_standard; + ///\} +}; +} //namespace ns3 + +#endif /* MESH_HELPER_H */ + diff --git a/src/helper/mesh-stack-installer.h b/src/helper/mesh-stack-installer.h new file mode 100644 index 000000000..fdd3ea7fc --- /dev/null +++ b/src/helper/mesh-stack-installer.h @@ -0,0 +1,46 @@ +/* -*- 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 + * + * Authors: Kirill Andreev + */ + + +#ifndef MESH_STACK_INSTALLER_H +#define MESH_STACK_INSTALLER_H +#include "ns3/mesh-point-device.h" +namespace ns3 { +/** + * \ingroup mesh + * + * \brief Prototype for class, which helps to install MAC-layer + * routing stack to ns3::MeshPointDevice + * \details You need to create a MeshPointDevice and attach all + * interfaces to it, than call Install method + */ +class MeshStack : public Object +{ + public: + ///\brief Installs mesh stack. needed by helper only + virtual bool InstallStack (Ptr mp) = 0; + /// Report statistics of a given mesh point + virtual void Report (const Ptr mp, std::ostream&) = 0; + /// Reset statistics of a given mesh point + virtual void ResetStats (const Ptr mp) = 0; +}; +} +#endif + diff --git a/src/helper/wscript b/src/helper/wscript index 142d90a46..115f83d7a 100644 --- a/src/helper/wscript +++ b/src/helper/wscript @@ -28,6 +28,9 @@ def build(bld): 'ipv4-global-routing-helper.cc', 'ipv4-list-routing-helper.cc', 'ipv4-routing-helper.cc', + 'mesh-helper.cc', + 'dot11s-installer.cc', + 'flame-installer.cc', 'athstats-helper.cc', 'ipv6-address-helper.cc', 'ipv6-interface-container.cc', @@ -66,6 +69,10 @@ def build(bld): 'ipv4-global-routing-helper.h', 'ipv4-list-routing-helper.h', 'ipv4-routing-helper.h', + 'mesh-helper.h', + 'mesh-stack-installer.h', + 'dot11s-installer.h', + 'flame-installer.h', 'athstats-helper.h', 'ipv6-address-helper.h', 'ipv6-interface-container.h', diff --git a/src/internet-stack/icmpv4.h b/src/internet-stack/icmpv4.h index 5e1ed4483..70935a611 100644 --- a/src/internet-stack/icmpv4.h +++ b/src/internet-stack/icmpv4.h @@ -1,3 +1,23 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 INRIA + * + * 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: Mathieu Lacage + */ + #ifndef ICMPV4_H #define ICMPV4_H diff --git a/src/internet-stack/icmpv6-header.h b/src/internet-stack/icmpv6-header.h index f867afedc..8cf5027f3 100644 --- a/src/internet-stack/icmpv6-header.h +++ b/src/internet-stack/icmpv6-header.h @@ -47,7 +47,7 @@ class Icmpv6Header : public Header ICMPV6_ERROR_PACKET_TOO_BIG, ICMPV6_ERROR_TIME_EXCEEDED, ICMPV6_ERROR_PARAMETER_ERROR, - ICMPV6_ECHO_REQUEST=128, + ICMPV6_ECHO_REQUEST = 128, ICMPV6_ECHO_REPLY, ICMPV6_SUBSCRIBE_REQUEST, ICMPV6_SUBSCRIBE_REPORT, diff --git a/src/internet-stack/icmpv6-l4-protocol.cc b/src/internet-stack/icmpv6-l4-protocol.cc index 71d6016b3..b783c6a93 100644 --- a/src/internet-stack/icmpv6-l4-protocol.cc +++ b/src/internet-stack/icmpv6-l4-protocol.cc @@ -278,7 +278,7 @@ void Icmpv6L4Protocol::ReceiveLLA (Icmpv6OptionLinkLayerAddress lla, Ipv6Address { NS_LOG_FUNCTION (this << lla << src << dst << interface); Address hardwareAddress; - NdiscCache::Entry* entry = NULL; + NdiscCache::Entry* entry = 0; Ptr cache = FindCache (interface->GetDevice ()); /* check if we have this address in our cache */ @@ -347,7 +347,7 @@ void Icmpv6L4Protocol::HandleRS (Ptr packet, Ipv6Address const &src, Ipv packet->RemoveHeader (rsHeader); Address hardwareAddress; Icmpv6OptionLinkLayerAddress lla (1); - NdiscCache::Entry* entry = NULL; + NdiscCache::Entry* entry = 0; Ptr cache = FindCache (interface->GetDevice ()); if (src != Ipv6Address::GetAny ()) @@ -664,7 +664,7 @@ void Icmpv6L4Protocol::HandleRedirection (Ptr packet, Ipv6Address const if (hasLla) { /* update the cache if needed */ - NdiscCache::Entry* entry = NULL; + NdiscCache::Entry* entry = 0; Ptr cache = FindCache (interface->GetDevice ()); entry = cache->Lookup (redirTarget); diff --git a/src/node/ipv6-address.cc b/src/node/ipv6-address.cc index 92049da87..818cb3a16 100644 --- a/src/node/ipv6-address.cc +++ b/src/node/ipv6-address.cc @@ -33,119 +33,141 @@ namespace ns3 { #ifdef __cplusplus extern "C" -{ +{ /* } */ #endif - /** - * \brief Get a hash key. - * \param k the key - * \param length the length of the key - * \param level the previous hash, or an arbitrary value - * \return hash - * \note Adpated from Jens Jakobsen implementation (chillispot). - */ - static uint32_t lookuphash (unsigned char* k, uint32_t length, uint32_t level) +/** + * \brief Get a hash key. + * \param k the key + * \param length the length of the key + * \param level the previous hash, or an arbitrary value + * \return hash + * \note Adapted from Jens Jakobsen implementation (chillispot). + */ +static uint32_t lookuphash (unsigned char* k, uint32_t length, uint32_t level) +{ +#define mix(a, b, c) \ +({ \ + (a) -= (b); (a) -= (c); (a) ^= ((c) >> 13); \ + (b) -= (c); (b) -= (a); (b) ^= ((a) << 8); \ + (c) -= (a); (c) -= (b); (c) ^= ((b) >> 13); \ + (a) -= (b); (a) -= (c); (a) ^= ((c) >> 12); \ + (b) -= (c); (b) -= (a); (b) ^= ((a) << 16); \ + (c) -= (a); (c) -= (b); (c) ^= ((b) >> 5); \ + (a) -= (b); (a) -= (c); (a) ^= ((c) >> 3); \ + (b) -= (c); (b) -= (a); (b) ^= ((a) << 10); \ + (c) -= (a); (c) -= (b); (c) ^= ((b) >> 15); \ +}) + + typedef uint32_t ub4; /* unsigned 4-byte quantities */ + typedef unsigned char ub1; /* unsigned 1-byte quantities */ + uint32_t a = 0; + uint32_t b = 0; + uint32_t c = 0; + uint32_t len = 0; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = level; /* the previous hash value */ + + /* handle most of the key */ + while (len >= 12) { -#define mix(a,b,c) \ - { \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<<8); \ - c -= a; c -= b; c ^= (b>>13); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<16); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>3); \ - b -= c; b -= a; b ^= (a<<10); \ - c -= a; c -= b; c ^= (b>>15); \ - } - - typedef uint32_t ub4; /* unsigned 4-byte quantities */ - typedef unsigned char ub1; /* unsigned 1-byte quantities */ - uint32_t a,b,c,len; - - /* Set up the internal state */ - len = length; - a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - c = level; /* the previous hash value */ - - /*---------------------------------------- handle most of the key */ - while (len >= 12) - { - a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); - b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); - c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); - mix(a, b, c); - k += 12; len -= 12; - } - - /*------------------------------------- handle the last 11 bytes */ - c += length; - switch (len) /* all the case statements fall through */ - { - case 11: c+=((ub4)k[10]<<24); - case 10: c+=((ub4)k[9]<<16); - case 9 : c+=((ub4)k[8]<<8); - /* the first byte of c is reserved for the length */ - case 8 : b+=((ub4)k[7]<<24); - case 7 : b+=((ub4)k[6]<<16); - case 6 : b+=((ub4)k[5]<<8); - case 5 : b+=k[4]; - case 4 : a+=((ub4)k[3]<<24); - case 3 : a+=((ub4)k[2]<<16); - case 2 : a+=((ub4)k[1]<<8); - case 1 : a+=k[0]; - /* case 0: nothing left to add */ - } - mix(a, b, c); - /*-------------------------------------------- report the result */ - return c; + a += (k[0] + ((ub4)k[1] << 8) + ((ub4)k[2] << 16) + ((ub4)k[3] << 24)); + b += (k[4] + ((ub4)k[5] << 8) + ((ub4)k[6] << 16) + ((ub4)k[7] << 24)); + c += (k[8] + ((ub4)k[9] << 8) + ((ub4)k[10] << 16) + ((ub4)k[11] << 24)); + mix (a, b, c); + k += 12; + len -= 12; } + + /* handle the last 11 bytes */ + c += length; + switch (len) /* all the case statements fall through */ + { + case 11: c += ((ub4)k[10] << 24); + case 10: c += ((ub4)k[9] << 16); + case 9 : c += ((ub4)k[8] << 8); /* the first byte of c is reserved for the length */ + case 8 : b += ((ub4)k[7] << 24); + case 7 : b += ((ub4)k[6] << 16); + case 6 : b += ((ub4)k[5] << 8); + case 5 : b += k[4]; + case 4 : a += ((ub4)k[3] << 24); + case 3 : a += ((ub4)k[2] << 16); + case 2 : a += ((ub4)k[1] << 8); + case 1 : a += k[0]; + /* case 0: nothing left to add */ + } + mix (a, b, c); + +#undef mix + + /* report the result */ + return c; +} + #ifdef __cplusplus } #endif /** * \brief Convert an IPv6 C-string into a 128-bit representation. - * \return 1 if OK, 0 if failure (bad format, ...) + * \return true if success, false otherwise (bad format, ...) * \note This function is strongly inspired by inet_pton6() from Paul Vixie. * \todo Handle IPv6 address with decimal value for last four bytes. */ -static int AsciiToIpv6Host (char const *address, uint8_t addr[16]) +static bool AsciiToIpv6Host (const char *address, uint8_t addr[16]) { - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - unsigned char tmp[16 /*NS_IN6ADDRSZ*/], *tp, *endp, *colonp; - const char *xdigits, *curtok; - int ch, seen_xdigits; - unsigned int val; + static const char xdigits_l[] = "0123456789abcdef"; + static const char xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[16]; + unsigned char* tp = tmp; + unsigned char* endp = 0; + unsigned char* colonp = 0; + const char* xdigits = 0; + const char* curtok = 0; + int ch = 0; + int seen_xdigits = 0; + unsigned int val = 0; + + memset (tp, 0x00, 16); + endp = tp + 16; - memset ((tp = tmp), '\0', 16 /* NS_IN6ADDRSZ*/); - endp = tp + 16 /*NS_IN6ADDRSZ*/; - colonp = NULL; /* Leading :: requires some special handling. */ if (*address == ':') + { if (*++address != ':') + { return (0); + } + } curtok = address; - seen_xdigits = 0; - val = 0; + while ((ch = *address++) != '\0') { - const char *pch; + const char *pch = 0; - if ((pch = strchr ((xdigits = xdigits_l), ch)) == NULL) + if ((pch = strchr ((xdigits = xdigits_l), ch)) == 0) + { pch = strchr ((xdigits = xdigits_u), ch); - if (pch != NULL) + } + + if (pch != 0) { val <<= 4; val |= (pch - xdigits); + if (++seen_xdigits > 4) + { return (0); + } continue; } if (ch == ':') { curtok = address; + if (!seen_xdigits) { if (colonp) @@ -153,8 +175,12 @@ static int AsciiToIpv6Host (char const *address, uint8_t addr[16]) colonp = tp; continue; } - if (tp + 2 /*NS_INT16SZ*/ > endp) + + if (tp + 2 > endp) + { return (0); + } + *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; seen_xdigits = 0; @@ -174,35 +200,45 @@ static int AsciiToIpv6Host (char const *address, uint8_t addr[16]) #endif return (0); } + if (seen_xdigits) { - if (tp + 2/* NS_INT16SZ*/ > endp) + if (tp + 2 > endp) + { return (0); + } *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; } - if (colonp != NULL) + + if (colonp != 0) { /* * Since some memmove ()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; - int i; + int i = 0; if (tp == endp) + { return (0); + } + for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } + tp = endp; } - if (tp != endp) - return (0); - /* memcpy (dst, tmp, NS_IN6ADDRSZ); */ + if (tp != endp) + { + return (0); + } + memcpy (addr, tmp, 16); return (1); } @@ -357,7 +393,7 @@ bool Ipv6Address::IsMulticast () const return false; } -Ipv6Address Ipv6Address::CombinePrefix (Ipv6Prefix const & prefix) +Ipv6Address Ipv6Address::CombinePrefix (Ipv6Prefix const& prefix) { Ipv6Address ipv6; uint8_t addr[16]; @@ -447,33 +483,21 @@ uint8_t Ipv6Address::GetType (void) return type; } -Ipv6Address Ipv6Address::GetZero () -{ - Ipv6Address zero ("::"); - return zero; -} - -Ipv6Address Ipv6Address::GetAny () -{ - Ipv6Address any ("::"); - return any; -} - Ipv6Address Ipv6Address::GetAllNodesMulticast () { - Ipv6Address nmc ("ff02::1"); + static Ipv6Address nmc ("ff02::1"); return nmc; } Ipv6Address Ipv6Address::GetAllRoutersMulticast () { - Ipv6Address rmc ("ff02::2"); + static Ipv6Address rmc ("ff02::2"); return rmc; } Ipv6Address Ipv6Address::GetAllHostsMulticast () { - Ipv6Address hmc ("ff02::3"); + static Ipv6Address hmc ("ff02::3"); return hmc; } @@ -483,6 +507,18 @@ Ipv6Address Ipv6Address::GetLoopback () return loopback; } +Ipv6Address Ipv6Address::GetZero () +{ + static Ipv6Address zero ("::"); + return zero; +} + +Ipv6Address Ipv6Address::GetAny () +{ + static Ipv6Address any ("::"); + return any; +} + Ipv6Address Ipv6Address::GetOnes () { static Ipv6Address ones ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); @@ -629,7 +665,7 @@ void Ipv6Prefix::Print (std::ostream &os) const Ipv6Prefix Ipv6Prefix::GetLoopback () { - Ipv6Prefix prefix ((uint8_t)128); + static Ipv6Prefix prefix ((uint8_t)128); return prefix; } @@ -641,7 +677,7 @@ Ipv6Prefix Ipv6Prefix::GetOnes () Ipv6Prefix Ipv6Prefix::GetZero () { - Ipv6Prefix prefix ((uint8_t)0); + static Ipv6Prefix prefix ((uint8_t)0); return prefix; } @@ -665,7 +701,7 @@ uint8_t Ipv6Prefix::GetPrefixLength () const prefixLength++; } } - + return prefixLength; } @@ -678,7 +714,7 @@ bool Ipv6Prefix::IsEqual (const Ipv6Prefix& other) const return false; } -std::ostream& operator<< (std::ostream& os, Ipv6Prefix const& prefix) +std::ostream& operator << (std::ostream& os, Ipv6Prefix const& prefix) { prefix.Print (os); return os; diff --git a/src/node/packetbb.cc b/src/node/packetbb.cc new file mode 100644 index 000000000..eaa60d070 --- /dev/null +++ b/src/node/packetbb.cc @@ -0,0 +1,2825 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* vim: set ts=2 sw=2 sta expandtab ai si cin: */ +/* + * Copyright (c) 2009 Drexel University + * + * 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: Tom Wambold + */ +/* These classes implement RFC 5444 - The Generalized Mobile Ad Hoc Network + * (MANET) Packet/PbbMessage Format + * See: http://tools.ietf.org/html/rfc5444 for details */ + +#include "ns3/ipv4-address.h" +#include "ns3/ipv6-address.h" +#include "ns3/assert.h" + +#include "packetbb.h" + +static const uint8_t VERSION = 0; +/* Packet flags */ +static const uint8_t PHAS_SEQ_NUM = 0x8; +static const uint8_t PHAS_TLV = 0x4; + +/* PbbMessage flags */ +static const uint8_t MHAS_ORIG = 0x80; +static const uint8_t MHAS_HOP_LIMIT = 0x40; +static const uint8_t MHAS_HOP_COUNT = 0x20; +static const uint8_t MHAS_SEQ_NUM = 0x10; + +/* Address block flags */ +static const uint8_t AHAS_HEAD = 0x80; +static const uint8_t AHAS_FULL_TAIL = 0x40; +static const uint8_t AHAS_ZERO_TAIL = 0x20; +static const uint8_t AHAS_SINGLE_PRE_LEN = 0x10; +static const uint8_t AHAS_MULTI_PRE_LEN = 0x08; + +/* TLV Flags */ +static const uint8_t THAS_TYPE_EXT = 0x80; +static const uint8_t THAS_SINGLE_INDEX = 0x40; +static const uint8_t THAS_MULTI_INDEX = 0x20; +static const uint8_t THAS_VALUE = 0x10; +static const uint8_t THAS_EXT_LEN = 0x08; +static const uint8_t TIS_MULTIVALUE = 0x04; + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (PbbPacket); + +PbbTlvBlock::Iterator +PbbTlvBlock::Begin (void) +{ + return m_tlvList.begin (); +} + +PbbTlvBlock::ConstIterator +PbbTlvBlock::Begin (void) const +{ + return m_tlvList.begin (); +} + +PbbTlvBlock::Iterator +PbbTlvBlock::End (void) +{ + return m_tlvList.end (); +} + +PbbTlvBlock::ConstIterator +PbbTlvBlock::End (void) const +{ + return m_tlvList.end (); +} + +int +PbbTlvBlock::Size (void) const +{ + return m_tlvList.size (); +} + +bool +PbbTlvBlock::Empty (void) const +{ + return m_tlvList.empty (); +} + +Ptr +PbbTlvBlock::Front (void) const +{ + return m_tlvList.front (); +} + +Ptr +PbbTlvBlock::Back (void) const +{ + return m_tlvList.back (); +} + +void +PbbTlvBlock::PushFront (Ptr tlv) +{ + m_tlvList.push_front (tlv); +} + +void +PbbTlvBlock::PopFront (void) +{ + m_tlvList.pop_front (); +} + +void +PbbTlvBlock::PushBack (Ptr tlv) +{ + m_tlvList.push_back (tlv); +} + +void +PbbTlvBlock::PopBack (void) +{ + m_tlvList.pop_back (); +} + +PbbTlvBlock::Iterator +PbbTlvBlock::Insert (PbbTlvBlock::Iterator position, const Ptr tlv) +{ + return m_tlvList.insert (position, tlv); +} + +PbbTlvBlock::Iterator +PbbTlvBlock::Erase (PbbTlvBlock::Iterator position) +{ + return m_tlvList.erase (position); +} + +PbbTlvBlock::Iterator +PbbTlvBlock::Erase (PbbTlvBlock::Iterator first, PbbTlvBlock::Iterator last) +{ + return m_tlvList.erase (first, last); +} + +void +PbbTlvBlock::Clear (void) +{ + m_tlvList.clear (); +} + +uint32_t +PbbTlvBlock::GetSerializedSize (void) const +{ + /* tlv size */ + uint32_t size = 2; + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + size += (*iter)->GetSerializedSize (); + } + return size; +} + +void +PbbTlvBlock::Serialize (Buffer::Iterator &start) const +{ + if (Empty ()) + { + start.WriteHtonU16 (0); + return; + } + + /* We need to write the size of the TLV block in front, so save its + * position. */ + Buffer::Iterator tlvsize = start; + start.Next (2); + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + (*iter)->Serialize (start); + } + /* - 2 to not include the size field */ + uint16_t size = start.GetDistanceFrom (tlvsize) - 2; + tlvsize.WriteHtonU16 (size); +} + +void +PbbTlvBlock::Deserialize (Buffer::Iterator &start) +{ + uint16_t size = start.ReadNtohU16 (); + + Buffer::Iterator tlvstart = start; + if (size > 0) + { + while (start.GetDistanceFrom (tlvstart) < size) + { + Ptr newtlv = Create (); + newtlv->Deserialize (start); + PushBack (newtlv); + } + } +} + +void +PbbTlvBlock::Print (std::ostream &os) const +{ + Print (os, 0); +} + +void +PbbTlvBlock::Print (std::ostream &os, int level) const +{ + std::string prefix = ""; + for (int i = 0; i < level; i++) + { + prefix.append("\t"); + } + + os << prefix << "TLV Block {" << std::endl; + os << prefix << "\tsize = " << Size () << std::endl; + os << prefix << "\tmembers [" << std::endl; + + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + (*iter)->Print (os, level+2); + } + + os << prefix << "\t]" << std::endl; + os << prefix << "}" << std::endl; +} + +bool +PbbTlvBlock::operator== (const PbbTlvBlock &other) const +{ + if (Size () != other.Size ()) + { + return false; + } + + ConstIterator ti, oi; + for (ti = Begin (), oi = other.Begin (); + ti != End () && oi != other.End (); + ti++, oi++) + { + if (**ti != **oi) + { + return false; + } + } + return true; +} + +bool +PbbTlvBlock::operator!= (const PbbTlvBlock &other) const +{ + return !(*this == other); +} + +/* End PbbTlvBlock class */ + +PbbAddressTlvBlock::Iterator +PbbAddressTlvBlock::Begin (void) +{ + return m_tlvList.begin (); +} + +PbbAddressTlvBlock::ConstIterator +PbbAddressTlvBlock::Begin (void) const +{ + return m_tlvList.begin (); +} + +PbbAddressTlvBlock::Iterator +PbbAddressTlvBlock::End (void) +{ + return m_tlvList.end (); +} + +PbbAddressTlvBlock::ConstIterator +PbbAddressTlvBlock::End (void) const +{ + return m_tlvList.end (); +} + +int +PbbAddressTlvBlock::Size (void) const +{ + return m_tlvList.size (); +} + +bool +PbbAddressTlvBlock::Empty (void) const +{ + return m_tlvList.empty (); +} + +Ptr +PbbAddressTlvBlock::Front (void) const +{ + return m_tlvList.front (); +} + +Ptr +PbbAddressTlvBlock::Back (void) const +{ + return m_tlvList.back (); +} + +void +PbbAddressTlvBlock::PushFront (Ptr tlv) +{ + m_tlvList.push_front (tlv); +} + +void +PbbAddressTlvBlock::PopFront (void) +{ + m_tlvList.pop_front (); +} + +void +PbbAddressTlvBlock::PushBack (Ptr tlv) +{ + m_tlvList.push_back (tlv); +} + +void +PbbAddressTlvBlock::PopBack (void) +{ + m_tlvList.pop_back (); +} + +PbbAddressTlvBlock::Iterator +PbbAddressTlvBlock::Insert (PbbAddressTlvBlock::Iterator position, const Ptr tlv) +{ + return m_tlvList.insert (position, tlv); +} + +PbbAddressTlvBlock::Iterator +PbbAddressTlvBlock::Erase (PbbAddressTlvBlock::Iterator position) +{ + return m_tlvList.erase (position); +} + +PbbAddressTlvBlock::Iterator +PbbAddressTlvBlock::Erase (PbbAddressTlvBlock::Iterator first, PbbAddressTlvBlock::Iterator last) +{ + return m_tlvList.erase (first, last); +} + +void +PbbAddressTlvBlock::Clear (void) +{ + m_tlvList.clear (); +} + +uint32_t +PbbAddressTlvBlock::GetSerializedSize (void) const +{ + /* tlv size */ + uint32_t size = 2; + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + size += (*iter)->GetSerializedSize (); + } + return size; +} + +void +PbbAddressTlvBlock::Serialize (Buffer::Iterator &start) const +{ + if (Empty ()) + { + start.WriteHtonU16 (0); + return; + } + + /* We need to write the size of the TLV block in front, so save its + * position. */ + Buffer::Iterator tlvsize = start; + start.Next (2); + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + (*iter)->Serialize (start); + } + /* - 2 to not include the size field */ + uint16_t size = start.GetDistanceFrom (tlvsize) - 2; + tlvsize.WriteHtonU16 (size); +} + +void +PbbAddressTlvBlock::Deserialize (Buffer::Iterator &start) +{ + uint16_t size = start.ReadNtohU16 (); + + Buffer::Iterator tlvstart = start; + if (size > 0) + { + while (start.GetDistanceFrom (tlvstart) < size) + { + Ptr newtlv = Create (); + newtlv->Deserialize (start); + PushBack (newtlv); + } + } +} + +void +PbbAddressTlvBlock::Print (std::ostream &os) const +{ + Print (os, 0); +} + +void +PbbAddressTlvBlock::Print (std::ostream &os, int level) const +{ + std::string prefix = ""; + for (int i = 0; i < level; i++) + { + prefix.append("\t"); + } + + os << prefix << "TLV Block {" << std::endl; + os << prefix << "\tsize = " << Size () << std::endl; + os << prefix << "\tmembers [" << std::endl; + + for (ConstIterator iter = Begin (); iter != End (); iter++) + { + (*iter)->Print (os, level+2); + } + + os << prefix << "\t]" << std::endl; + os << prefix << "}" << std::endl; +} + +bool +PbbAddressTlvBlock::operator== (const PbbAddressTlvBlock &other) const +{ + if (Size () != other.Size ()) + { + return false; + } + + ConstIterator it, ot; + for (it = Begin (), ot = other.Begin (); + it != End () && ot != other.End (); + it++, ot++) + { + if (**it != **ot) + { + return false; + } + } + return true; +} + +bool +PbbAddressTlvBlock::operator!= (const PbbAddressTlvBlock &other) const +{ + return !(*this == other); +} + + +/* End PbbAddressTlvBlock Class */ + +PbbPacket::PbbPacket (void) +{ + m_refCount = 1; + m_version = VERSION; + m_hasseqnum = false; +} + +uint8_t +PbbPacket::GetVersion (void) const +{ + return m_version; +} + +void +PbbPacket::SetSequenceNumber (uint16_t number) +{ + m_seqnum = number; + m_hasseqnum = true; +} + +uint16_t +PbbPacket::GetSequenceNumber (void) const +{ + NS_ASSERT (HasSequenceNumber ()); + return m_seqnum; +} + +bool +PbbPacket::HasSequenceNumber (void) const +{ + return m_hasseqnum; +} + +/* Manipulating Packet TLVs */ + +PbbPacket::TlvIterator +PbbPacket::TlvBegin (void) +{ + return m_tlvList.Begin (); +} + +PbbPacket::ConstTlvIterator +PbbPacket::TlvBegin (void) const +{ + return m_tlvList.Begin (); +} + +PbbPacket::TlvIterator +PbbPacket::TlvEnd (void) +{ + return m_tlvList.End (); +} + +PbbPacket::ConstTlvIterator +PbbPacket::TlvEnd (void) const +{ + return m_tlvList.End (); +} + +int +PbbPacket::TlvSize (void) const +{ + return m_tlvList.Size (); +} + +bool +PbbPacket::TlvEmpty (void) const +{ + return m_tlvList.Empty (); +} + +Ptr +PbbPacket::TlvFront (void) +{ + return m_tlvList.Front (); +} + +const Ptr +PbbPacket::TlvFront (void) const +{ + return m_tlvList.Front (); +} + +Ptr +PbbPacket::TlvBack (void) +{ + return m_tlvList.Back (); +} + +const Ptr +PbbPacket::TlvBack (void) const +{ + return m_tlvList.Back (); +} + +void +PbbPacket::TlvPushFront (Ptr tlv) +{ + m_tlvList.PushFront (tlv); +} + +void +PbbPacket::TlvPopFront (void) +{ + m_tlvList.PopFront (); +} + +void +PbbPacket::TlvPushBack (Ptr tlv) +{ + m_tlvList.PushBack (tlv); +} + +void +PbbPacket::TlvPopBack (void) +{ + m_tlvList.PopBack (); +} + +PbbPacket::TlvIterator +PbbPacket::Erase (PbbPacket::TlvIterator position) +{ + return m_tlvList.Erase (position); +} + +PbbPacket::TlvIterator +PbbPacket::Erase (PbbPacket::TlvIterator first, PbbPacket::TlvIterator last) +{ + return m_tlvList.Erase (first, last); +} + +void +PbbPacket::TlvClear (void) +{ + m_tlvList.Clear (); +} + +/* Manipulating Packet Messages */ + +PbbPacket::MessageIterator +PbbPacket::MessageBegin (void) +{ + return m_messageList.begin (); +} + +PbbPacket::ConstMessageIterator +PbbPacket::MessageBegin (void) const +{ + return m_messageList.begin (); +} + +PbbPacket::MessageIterator +PbbPacket::MessageEnd (void) +{ + return m_messageList.end (); +} + +PbbPacket::ConstMessageIterator +PbbPacket::MessageEnd (void) const +{ + return m_messageList.end (); +} + +int +PbbPacket::MessageSize (void) const +{ + return m_messageList.size (); +} + +bool +PbbPacket::MessageEmpty (void) const +{ + return m_messageList.empty (); +} + +Ptr +PbbPacket::MessageFront (void) +{ + return m_messageList.front (); +} + +const Ptr +PbbPacket::MessageFront (void) const +{ + return m_messageList.front (); +} + +Ptr +PbbPacket::MessageBack (void) +{ + return m_messageList.back (); +} + +const Ptr +PbbPacket::MessageBack (void) const +{ + return m_messageList.back (); +} + +void +PbbPacket::MessagePushFront (Ptr tlv) +{ + m_messageList.push_front (tlv); +} + +void +PbbPacket::MessagePopFront (void) +{ + m_messageList.pop_front (); +} + +void +PbbPacket::MessagePushBack (Ptr tlv) +{ + m_messageList.push_back (tlv); +} + +void +PbbPacket::MessagePopBack (void) +{ + m_messageList.pop_back (); +} + +PbbPacket::MessageIterator +PbbPacket::Erase (PbbPacket::MessageIterator position) +{ + return m_messageList.erase (position); +} + +PbbPacket::MessageIterator +PbbPacket::Erase (PbbPacket::MessageIterator first, + PbbPacket::MessageIterator last) +{ + return m_messageList.erase (first, last); +} + +void +PbbPacket::MessageClear (void) +{ + m_messageList.clear (); +} + +void +PbbPacket::Ref (void) const +{ + m_refCount++; +} + +void +PbbPacket::Unref (void) const +{ + m_refCount--; + if (m_refCount == 0) + { + delete this; + } +} + +TypeId +PbbPacket::GetTypeId (void) +{ + static TypeId tid = TypeId ("PbbPacket") + .SetParent
    () + .AddConstructor () + ; + return tid; +} + +TypeId +PbbPacket::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +PbbPacket::GetSerializedSize (void) const +{ + /* Version number + flags */ + uint32_t size = 1; + + if (HasSequenceNumber()) + { + size += 2; + } + + if (!TlvEmpty ()) + { + size += m_tlvList.GetSerializedSize (); + } + + for (ConstMessageIterator iter = MessageBegin (); + iter != MessageEnd (); + iter++) + { + size += (*iter)->GetSerializedSize (); + } + + return size; +} + +void +PbbPacket::Serialize (Buffer::Iterator start) const +{ + /* We remember the start, so we can write the flags after we check for a + * sequence number and TLV. */ + Buffer::Iterator bufref = start; + start.Next (); + + uint8_t flags = VERSION; + /* Make room for 4 bit flags */ + flags <<= 4; + + if (HasSequenceNumber ()) + { + flags |= PHAS_SEQ_NUM; + start.WriteHtonU16 (GetSequenceNumber ()); + } + + if (!TlvEmpty ()) + { + flags |= PHAS_TLV; + m_tlvList.Serialize (start); + } + + bufref.WriteU8(flags); + + for (ConstMessageIterator iter = MessageBegin (); + iter != MessageEnd (); + iter++) + { + (*iter)->Serialize (start); + } +} + +uint32_t +PbbPacket::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator begin = start; + + uint8_t flags = start.ReadU8 (); + + if (flags & PHAS_SEQ_NUM) + { + SetSequenceNumber (start.ReadNtohU16 ()); + } + + if (flags & PHAS_TLV) + { + m_tlvList.Deserialize (start); + } + + while (!start.IsEnd()) + { + Ptr newmsg = PbbMessage::DeserializeMessage (start); + if (newmsg == 0) + { + return start.GetDistanceFrom (begin); + } + MessagePushBack (newmsg); + } + + flags >>= 4; + m_version = flags; + + return start.GetDistanceFrom (begin); +} + +void +PbbPacket::Print (std::ostream &os) const +{ + os << "PbbPacket {" << std::endl; + + if (HasSequenceNumber ()) + { + os << "\tsequence number = " << GetSequenceNumber (); + } + + os << std::endl; + + m_tlvList.Print (os, 1); + + for (ConstMessageIterator iter = MessageBegin (); + iter != MessageEnd (); + iter++) + { + (*iter)->Print (os, 1); + } + + os << "}" << std::endl; +} + +bool +PbbPacket::operator== (const PbbPacket &other) const +{ + if (GetVersion () != other.GetVersion ()) + { + return false; + } + + if (HasSequenceNumber () != other.HasSequenceNumber ()) + { + return false; + } + + if (HasSequenceNumber ()) + { + if (GetSequenceNumber () != other.GetSequenceNumber ()) + return false; + } + + if (m_tlvList != other.m_tlvList) + { + return false; + } + + if (MessageSize () != other.MessageSize ()) + { + return false; + } + + ConstMessageIterator tmi, omi; + for (tmi = MessageBegin (), omi = other.MessageBegin (); + tmi != MessageEnd () && omi != other.MessageEnd (); + tmi++, omi++) + { + if (**tmi != **omi) + { + return false; + } + } + return true; +} + +bool +PbbPacket::operator!= (const PbbPacket &other) const +{ + return !(*this == other); +} + +/* End PbbPacket class */ + +PbbMessage::PbbMessage () +{ + m_refCount = 1; + /* Default to IPv4 */ + m_addrSize = IPV4; + m_hasOriginatorAddress = false; + m_hasHopLimit = false; + m_hasHopCount = false; + m_hasSequenceNumber = false; +} + +PbbMessage::~PbbMessage () +{ +} + +void +PbbMessage::SetType (uint8_t type) +{ + m_type = type; +} + +uint8_t +PbbMessage::GetType (void) const +{ + return m_type; +} + +PbbAddressLength +PbbMessage::GetAddressLength (void) const +{ + return m_addrSize; +} + +void +PbbMessage::SetOriginatorAddress (Address address) +{ + m_originatorAddress = address; + m_hasOriginatorAddress = true; +} + +Address +PbbMessage::GetOriginatorAddress (void) const +{ + NS_ASSERT (HasOriginatorAddress ()); + return m_originatorAddress; +} + +bool +PbbMessage::HasOriginatorAddress (void) const +{ + return m_hasOriginatorAddress; +} + +void +PbbMessage::SetHopLimit (uint8_t hopLimit) +{ + m_hopLimit = hopLimit; + m_hasHopLimit = true; +} + +uint8_t +PbbMessage::GetHopLimit (void) const +{ + NS_ASSERT (HasHopLimit ()); + return m_hopLimit; +} + +bool +PbbMessage::HasHopLimit (void) const +{ + return m_hasHopLimit; +} + +void +PbbMessage::SetHopCount (uint8_t hopCount) +{ + m_hopCount = hopCount; + m_hasHopCount = true; +} + +uint8_t +PbbMessage::GetHopCount (void) const +{ + NS_ASSERT (HasHopCount ()); + return m_hopCount; +} + +bool +PbbMessage::HasHopCount (void) const +{ + return m_hasHopCount; +} + +void +PbbMessage::SetSequenceNumber (uint16_t sequenceNumber) +{ + m_sequenceNumber = sequenceNumber; + m_hasSequenceNumber = true; +} + +uint16_t +PbbMessage::GetSequenceNumber (void) const +{ + NS_ASSERT (HasSequenceNumber ()); + return m_sequenceNumber; +} + +bool +PbbMessage::HasSequenceNumber (void) const +{ + return m_hasSequenceNumber; +} + +/* Manipulating PbbMessage TLVs */ + +PbbMessage::TlvIterator +PbbMessage::TlvBegin (void) +{ + return m_tlvList.Begin(); +} + +PbbMessage::ConstTlvIterator +PbbMessage::TlvBegin (void) const +{ + return m_tlvList.Begin(); +} + +PbbMessage::TlvIterator +PbbMessage::TlvEnd (void) +{ + return m_tlvList.End(); +} + +PbbMessage::ConstTlvIterator +PbbMessage::TlvEnd (void) const +{ + return m_tlvList.End(); +} + +int +PbbMessage::TlvSize (void) const +{ + return m_tlvList.Size(); +} + +bool +PbbMessage::TlvEmpty (void) const +{ + return m_tlvList.Empty(); +} + +Ptr +PbbMessage::TlvFront (void) +{ + return m_tlvList.Front(); +} + +const Ptr +PbbMessage::TlvFront (void) const +{ + return m_tlvList.Front(); +} + +Ptr +PbbMessage::TlvBack (void) +{ + return m_tlvList.Back(); +} + +const Ptr +PbbMessage::TlvBack (void) const +{ + return m_tlvList.Back(); +} + +void +PbbMessage::TlvPushFront (Ptr tlv) +{ + m_tlvList.PushFront(tlv); +} + +void +PbbMessage::TlvPopFront (void) +{ + m_tlvList.PopFront(); +} + +void +PbbMessage::TlvPushBack (Ptr tlv) +{ + m_tlvList.PushBack(tlv); +} + +void +PbbMessage::TlvPopBack (void) +{ + m_tlvList.PopBack(); +} + +PbbMessage::TlvIterator +PbbMessage::TlvErase (PbbMessage::TlvIterator position) +{ + return m_tlvList.Erase(position); +} + +PbbMessage::TlvIterator +PbbMessage::TlvErase (PbbMessage::TlvIterator first, PbbMessage::TlvIterator last) +{ + return m_tlvList.Erase(first, last); +} + +void +PbbMessage::TlvClear (void) +{ + return m_tlvList.Clear(); +} + +/* Manipulating Address Block and Address TLV pairs */ + +PbbMessage::AddressBlockIterator +PbbMessage::AddressBlockBegin (void) +{ + return m_addressBlockList.begin(); +} + +PbbMessage::ConstAddressBlockIterator +PbbMessage::AddressBlockBegin (void) const +{ + return m_addressBlockList.begin(); +} + +PbbMessage::AddressBlockIterator +PbbMessage::AddressBlockEnd (void) +{ + return m_addressBlockList.end(); +} + +PbbMessage::ConstAddressBlockIterator +PbbMessage::AddressBlockEnd (void) const +{ + return m_addressBlockList.end(); +} + +int +PbbMessage::AddressBlockSize (void) const +{ + return m_addressBlockList.size(); +} + +bool +PbbMessage::AddressBlockEmpty (void) const +{ + return m_addressBlockList.empty(); +} + +Ptr +PbbMessage::AddressBlockFront (void) +{ + return m_addressBlockList.front(); +} + +const Ptr +PbbMessage::AddressBlockFront (void) const +{ + return m_addressBlockList.front(); +} + +Ptr +PbbMessage::AddressBlockBack (void) +{ + return m_addressBlockList.back(); +} + +const Ptr +PbbMessage::AddressBlockBack (void) const +{ + return m_addressBlockList.back(); +} + +void +PbbMessage::AddressBlockPushFront (Ptr tlv) +{ + m_addressBlockList.push_front(tlv); +} + +void +PbbMessage::AddressBlockPopFront (void) +{ + m_addressBlockList.pop_front(); +} + +void +PbbMessage::AddressBlockPushBack (Ptr tlv) +{ + m_addressBlockList.push_back(tlv); +} + +void +PbbMessage::AddressBlockPopBack (void) +{ + m_addressBlockList.pop_back(); +} + +PbbMessage::AddressBlockIterator +PbbMessage::AddressBlockErase (PbbMessage::AddressBlockIterator position) +{ + return m_addressBlockList.erase(position); +} + +PbbMessage::AddressBlockIterator +PbbMessage::AddressBlockErase (PbbMessage::AddressBlockIterator first, + PbbMessage::AddressBlockIterator last) +{ + return m_addressBlockList.erase(first, last); +} + +void +PbbMessage::AddressBlockClear (void) +{ + return m_addressBlockList.clear(); +} + +void +PbbMessage::Ref (void) const +{ + m_refCount++; +} + +void +PbbMessage::Unref (void) const +{ + m_refCount--; + if (m_refCount == 0) + { + delete this; + } +} + +uint32_t +PbbMessage::GetSerializedSize (void) const +{ + /* msg-type + (msg-flags + msg-addr-length) + 2msg-size */ + uint32_t size = 4; + + if (HasOriginatorAddress()) + { + size += GetAddressLength() + 1; + } + + if (HasHopLimit()) + { + size++; + } + + if (HasHopCount()) + { + size++; + } + + if (HasSequenceNumber()) + { + size += 2; + } + + size += m_tlvList.GetSerializedSize (); + + for (ConstAddressBlockIterator iter = AddressBlockBegin (); + iter != AddressBlockEnd (); + iter++) + { + size += (*iter)->GetSerializedSize (); + } + + return size; +} + +void +PbbMessage::Serialize (Buffer::Iterator &start) const +{ + Buffer::Iterator front = start; + + start.WriteU8 (GetType()); + + /* Save a reference to the spot where we will later write the flags */ + Buffer::Iterator bufref = start; + start.Next (1); + + uint8_t flags = 0; + + flags = GetAddressLength (); + + Buffer::Iterator sizeref = start; + start.Next (2); + + if (HasOriginatorAddress ()) + { + flags |= MHAS_ORIG; + SerializeOriginatorAddress (start); + } + + if (HasHopLimit ()) + { + flags |= MHAS_HOP_LIMIT; + start.WriteU8 (GetHopLimit ()); + } + + if (HasHopCount ()) + { + flags |= MHAS_HOP_COUNT; + start.WriteU8 (GetHopCount ()); + } + + if (HasSequenceNumber ()) + { + flags |= MHAS_SEQ_NUM; + start.WriteHtonU16 (GetSequenceNumber ()); + } + + bufref.WriteU8(flags); + + m_tlvList.Serialize (start); + + for (ConstAddressBlockIterator iter = AddressBlockBegin (); + iter != AddressBlockEnd (); + iter++) + { + (*iter)->Serialize (start); + } + + sizeref.WriteHtonU16 (front.GetDistanceFrom (start)); +} + +Ptr +PbbMessage::DeserializeMessage (Buffer::Iterator &start) +{ + /* We need to read the msg-addr-len field to determine what kind of object to + * construct. */ + start.Next (); + uint8_t addrlen = start.ReadU8 (); + start.Prev (2); /* Go back to the start */ + + /* The first four bytes of the flag is the address length. Set the last four + * bytes to 0 to read it. */ + addrlen = (addrlen & 0xf); + + Ptr newmsg; + + switch (addrlen) + { + case 0: + case IPV4: + newmsg = Create (); + break; + case IPV6: + newmsg = Create (); + break; + default: + return 0; + break; + } + newmsg->Deserialize (start); + return newmsg; +} + +void +PbbMessage::Deserialize (Buffer::Iterator &start) +{ + Buffer::Iterator front = start; + SetType (start.ReadU8 ()); + uint8_t flags = start.ReadU8 (); + + uint16_t size = start.ReadNtohU16 (); + + if (flags & MHAS_ORIG) + { + SetOriginatorAddress (DeserializeOriginatorAddress (start)); + } + + if (flags & MHAS_HOP_LIMIT) + { + SetHopLimit (start.ReadU8 ()); + } + + if (flags & MHAS_HOP_COUNT) + { + SetHopCount (start.ReadU8 ()); + } + + if (flags & MHAS_SEQ_NUM) + { + SetSequenceNumber (start.ReadNtohU16 ()); + } + + m_tlvList.Deserialize (start); + + if (size > 0) + { + while (start.GetDistanceFrom(front) < size) + { + Ptr newab = AddressBlockDeserialize (start); + AddressBlockPushBack (newab); + } + } +} + +void +PbbMessage::Print (std::ostream &os) const +{ + Print (os, 0); +} + +void +PbbMessage::Print (std::ostream &os, int level) const +{ + std::string prefix = ""; + for (int i = 0; i < level; i++) + { + prefix.append ("\t"); + } + + os << prefix << "PbbMessage {" << std::endl; + + os << prefix << "\tmessage type = " << (int)GetType () << std::endl; + os << prefix << "\taddress size = " << GetAddressLength () << std::endl; + + if (HasOriginatorAddress ()) + { + os << prefix << "\toriginator address = "; + PrintOriginatorAddress (os); + os << std::endl; + } + + if (HasHopLimit ()) + { + os << prefix << "\thop limit = " << (int)GetHopLimit () << std::endl; + } + + if (HasHopCount ()) + { + os << prefix << "\thop count = " << (int)GetHopCount () << std::endl; + } + + if (HasSequenceNumber ()) + { + os << prefix << "\tseqnum = " << GetSequenceNumber () << std::endl; + } + + m_tlvList.Print (os, level+1); + + for (ConstAddressBlockIterator iter = AddressBlockBegin (); + iter != AddressBlockEnd (); + iter++) + { + (*iter)->Print (os, level+1); + } + os << prefix << "}" << std::endl; +} + +bool +PbbMessage::operator== (const PbbMessage &other) const +{ + if (GetAddressLength () != other.GetAddressLength ()) + { + return false; + } + + if (GetType () != other.GetType ()) + { + return false; + } + + if (HasOriginatorAddress () != other.HasOriginatorAddress ()) + { + return false; + } + + if (HasOriginatorAddress ()) + { + if (GetOriginatorAddress () != other.GetOriginatorAddress ()) + { + return false; + } + } + + if (HasHopLimit () != other.HasHopLimit ()) + { + return false; + } + + if (HasHopLimit ()) + { + if (GetHopLimit () != other.GetHopLimit ()) + { + return false; + } + } + + if (HasHopCount () != other.HasHopCount ()) + { + return false; + } + + if (HasHopCount ()) + { + if (GetHopCount () != other.GetHopCount ()) + { + return false; + } + } + + if (HasSequenceNumber () != other.HasSequenceNumber ()) + { + return false; + } + + if (HasSequenceNumber ()) + { + if (GetSequenceNumber () != other.GetSequenceNumber ()) + { + return false; + } + } + + if (m_tlvList != other.m_tlvList) + { + return false; + } + + if (AddressBlockSize () != other.AddressBlockSize ()) + { + return false; + } + + ConstAddressBlockIterator tai, oai; + for (tai = AddressBlockBegin (), oai = other.AddressBlockBegin (); + tai != AddressBlockEnd () && oai != other.AddressBlockEnd (); + tai++, oai++) + { + if (**tai != **oai) + { + return false; + } + } + return true; +} + +bool +PbbMessage::operator!= (const PbbMessage &other) const +{ + return !(*this == other); +} + +/* End PbbMessage Class */ + +PbbMessageIpv4::PbbMessageIpv4 () +{ +} + +PbbMessageIpv4::~PbbMessageIpv4 () +{ +} + +PbbAddressLength +PbbMessageIpv4::GetAddressLength (void) const +{ + return IPV4; +} + +void +PbbMessageIpv4::SerializeOriginatorAddress (Buffer::Iterator &start) const +{ + uint8_t buffer[GetAddressLength () + 1]; + Ipv4Address::ConvertFrom (GetOriginatorAddress ()).Serialize(buffer); + start.Write (buffer, GetAddressLength () + 1); +} + +Address +PbbMessageIpv4::DeserializeOriginatorAddress (Buffer::Iterator &start) const +{ + uint8_t buffer[GetAddressLength () + 1]; + start.Read(buffer, GetAddressLength () + 1); + return Ipv4Address::Deserialize (buffer); +} + +void +PbbMessageIpv4::PrintOriginatorAddress (std::ostream &os) const +{ + Ipv4Address::ConvertFrom (GetOriginatorAddress ()).Print (os); +} + +Ptr +PbbMessageIpv4::AddressBlockDeserialize (Buffer::Iterator &start) const +{ + Ptr newab = Create (); + newab->Deserialize (start); + return newab; +} + +/* End PbbMessageIpv4 Class */ + +PbbMessageIpv6::PbbMessageIpv6 () +{ +} + +PbbMessageIpv6::~PbbMessageIpv6 () +{ +} + +PbbAddressLength +PbbMessageIpv6::GetAddressLength (void) const +{ + return IPV6; +} + +void +PbbMessageIpv6::SerializeOriginatorAddress (Buffer::Iterator &start) const +{ + uint8_t buffer[GetAddressLength () + 1]; + Ipv6Address::ConvertFrom (GetOriginatorAddress ()).Serialize(buffer); + start.Write (buffer, GetAddressLength () + 1); +} + +Address +PbbMessageIpv6::DeserializeOriginatorAddress (Buffer::Iterator &start) const +{ + uint8_t buffer[GetAddressLength () + 1]; + start.Read(buffer, GetAddressLength () + 1); + return Ipv6Address::Deserialize (buffer); +} + +void +PbbMessageIpv6::PrintOriginatorAddress (std::ostream &os) const +{ + Ipv6Address::ConvertFrom (GetOriginatorAddress ()).Print (os); +} + +Ptr +PbbMessageIpv6::AddressBlockDeserialize (Buffer::Iterator &start) const +{ + Ptr newab = Create (); + newab->Deserialize (start); + return newab; +} + +/* End PbbMessageIpv6 Class */ + +PbbAddressBlock::PbbAddressBlock () +{ + m_refCount = 1; +} + +PbbAddressBlock::~PbbAddressBlock () +{ +} + +/* Manipulating the address block */ + +PbbAddressBlock::AddressIterator +PbbAddressBlock::AddressBegin (void) +{ + return m_addressList.begin(); +} + +PbbAddressBlock::ConstAddressIterator +PbbAddressBlock::AddressBegin (void) const +{ + return m_addressList.begin(); +} + +PbbAddressBlock::AddressIterator +PbbAddressBlock::AddressEnd (void) +{ + return m_addressList.end(); +} + +PbbAddressBlock::ConstAddressIterator +PbbAddressBlock::AddressEnd (void) const +{ + return m_addressList.end(); +} + +int +PbbAddressBlock::AddressSize (void) const +{ + return m_addressList.size(); +} + +bool +PbbAddressBlock::AddressEmpty (void) const +{ + return m_addressList.empty(); +} + +Address +PbbAddressBlock::AddressFront (void) const +{ + return m_addressList.front(); +} + +Address +PbbAddressBlock::AddressBack (void) const +{ + return m_addressList.back(); +} + +void +PbbAddressBlock::AddressPushFront (Address tlv) +{ + m_addressList.push_front(tlv); +} + +void +PbbAddressBlock::AddressPopFront (void) +{ + m_addressList.pop_front(); +} + +void +PbbAddressBlock::AddressPushBack (Address tlv) +{ + m_addressList.push_back(tlv); +} + +void +PbbAddressBlock::AddressPopBack (void) +{ + m_addressList.pop_back(); +} + +PbbAddressBlock::AddressIterator +PbbAddressBlock::AddressErase (PbbAddressBlock::AddressIterator position) +{ + return m_addressList.erase(position); +} + +PbbAddressBlock::AddressIterator +PbbAddressBlock::AddressErase (PbbAddressBlock::AddressIterator first, + PbbAddressBlock::AddressIterator last) +{ + return m_addressList.erase(first, last); +} + + void +PbbAddressBlock::AddressClear (void) +{ + return m_addressList.clear(); +} + +/* Manipulating the prefix list */ + +PbbAddressBlock::PrefixIterator +PbbAddressBlock::PrefixBegin (void) +{ + return m_prefixList.begin (); +} + +PbbAddressBlock::ConstPrefixIterator +PbbAddressBlock::PrefixBegin (void) const +{ + return m_prefixList.begin (); +} + +PbbAddressBlock::PrefixIterator +PbbAddressBlock::PrefixEnd (void) +{ + return m_prefixList.end (); +} + +PbbAddressBlock::ConstPrefixIterator +PbbAddressBlock::PrefixEnd (void) const +{ + return m_prefixList.end (); +} + +int +PbbAddressBlock::PrefixSize (void) const +{ + return m_prefixList.size (); +} + +bool +PbbAddressBlock::PrefixEmpty (void) const +{ + return m_prefixList.empty (); +} + +uint8_t +PbbAddressBlock::PrefixFront (void) const +{ + return m_prefixList.front (); +} + +uint8_t +PbbAddressBlock::PrefixBack (void) const +{ + return m_prefixList.back (); +} + +void +PbbAddressBlock::PrefixPushFront (uint8_t prefix) +{ + m_prefixList.push_front (prefix); +} + +void +PbbAddressBlock::PrefixPopFront (void) +{ + m_prefixList.pop_front (); +} + +void +PbbAddressBlock::PrefixPushBack (uint8_t prefix) +{ + m_prefixList.push_back (prefix); +} + +void +PbbAddressBlock::PrefixPopBack (void) +{ + m_prefixList.pop_back (); +} + +PbbAddressBlock::PrefixIterator +PbbAddressBlock::PrefixInsert (PbbAddressBlock::PrefixIterator position, const uint8_t value) +{ + return m_prefixList.insert (position, value); +} + +PbbAddressBlock::PrefixIterator +PbbAddressBlock::PrefixErase (PbbAddressBlock::PrefixIterator position) +{ + return m_prefixList.erase (position); +} + +PbbAddressBlock::PrefixIterator +PbbAddressBlock::PrefixErase (PbbAddressBlock::PrefixIterator first, PbbAddressBlock::PrefixIterator last) +{ + return m_prefixList.erase (first, last); +} + +void +PbbAddressBlock::PrefixClear (void) +{ + m_prefixList.clear (); +} + +/* Manipulating the TLV block */ + +PbbAddressBlock::TlvIterator +PbbAddressBlock::TlvBegin (void) +{ + return m_addressTlvList.Begin(); +} + +PbbAddressBlock::ConstTlvIterator +PbbAddressBlock::TlvBegin (void) const +{ + return m_addressTlvList.Begin(); +} + +PbbAddressBlock::TlvIterator +PbbAddressBlock::TlvEnd (void) +{ + return m_addressTlvList.End(); +} + +PbbAddressBlock::ConstTlvIterator +PbbAddressBlock::TlvEnd (void) const +{ + return m_addressTlvList.End(); +} + +int +PbbAddressBlock::TlvSize (void) const +{ + return m_addressTlvList.Size(); +} + +bool +PbbAddressBlock::TlvEmpty (void) const +{ + return m_addressTlvList.Empty(); +} + +Ptr +PbbAddressBlock::TlvFront (void) +{ + return m_addressTlvList.Front(); +} + +const Ptr +PbbAddressBlock::TlvFront (void) const +{ + return m_addressTlvList.Front(); +} + +Ptr +PbbAddressBlock::TlvBack (void) +{ + return m_addressTlvList.Back(); +} + +const Ptr +PbbAddressBlock::TlvBack (void) const +{ + return m_addressTlvList.Back(); +} + +void +PbbAddressBlock::TlvPushFront (Ptr tlv) +{ + m_addressTlvList.PushFront(tlv); +} + +void +PbbAddressBlock::TlvPopFront (void) +{ + m_addressTlvList.PopFront(); +} + +void +PbbAddressBlock::TlvPushBack (Ptr tlv) +{ + m_addressTlvList.PushBack(tlv); +} + +void +PbbAddressBlock::TlvPopBack (void) +{ + m_addressTlvList.PopBack(); +} + +PbbAddressBlock::TlvIterator +PbbAddressBlock::TlvErase (PbbAddressBlock::TlvIterator position) +{ + return m_addressTlvList.Erase(position); +} + +PbbAddressBlock::TlvIterator +PbbAddressBlock::TlvErase (PbbAddressBlock::TlvIterator first, + PbbAddressBlock::TlvIterator last) +{ + return m_addressTlvList.Erase(first, last); +} + +void +PbbAddressBlock::TlvClear (void) +{ + return m_addressTlvList.Clear(); +} + +void +PbbAddressBlock::Ref (void) const +{ + m_refCount++; +} + +void +PbbAddressBlock::Unref (void) const +{ + m_refCount--; + if (m_refCount == 0) + { + delete this; + } +} + +uint32_t +PbbAddressBlock::GetSerializedSize (void) const +{ + /* num-addr + flags */ + uint32_t size = 2; + + if (AddressSize () == 1) + { + size += GetAddressLength () + PrefixSize(); + } + else if (AddressSize () > 0) + { + uint8_t head[GetAddressLength ()]; + uint8_t headlen = 0; + uint8_t tail[GetAddressLength ()]; + uint8_t taillen = 0; + + GetHeadTail (head, headlen, tail, taillen); + + if (headlen > 0) + { + size += 1 + headlen; + } + + if (taillen > 0) + { + size++; + if (!HasZeroTail (tail, taillen)) + { + size += taillen; + } + } + + /* mid size */ + size += (GetAddressLength () - headlen - taillen) * AddressSize (); + + size += PrefixSize (); + } + + size += m_addressTlvList.GetSerializedSize (); + + return size; +} + +void +PbbAddressBlock::Serialize (Buffer::Iterator &start) const +{ + start.WriteU8 (AddressSize ()); + + if (AddressSize () == 1) + { + start.WriteU8 (0); + + uint8_t buf[GetAddressLength ()]; + SerializeAddress (buf, AddressBegin ()); + start.Write (buf, GetAddressLength ()); + + if (PrefixSize () == 1) + { + start.WriteU8 (PrefixFront ()); + } + } + else if (AddressSize () > 0) + { + Buffer::Iterator bufref = start; + uint8_t flags = 0; + start.Next (); + + uint8_t head[GetAddressLength ()]; + uint8_t tail[GetAddressLength ()]; + uint8_t headlen = 0; + uint8_t taillen = 0; + + GetHeadTail (head, headlen, tail, taillen); + + if (headlen > 0) + { + flags |= AHAS_HEAD; + start.WriteU8 (headlen); + start.Write (head, headlen); + } + + if (taillen > 0) + { + start.WriteU8 (taillen); + + if (HasZeroTail (tail, taillen)) + { + flags |= AHAS_ZERO_TAIL; + } + else + { + flags |= AHAS_FULL_TAIL; + start.Write (tail, taillen); + } + } + + if (headlen + taillen < GetAddressLength ()) + { + uint8_t mid[GetAddressLength ()]; + for (PbbAddressBlock::ConstAddressIterator iter = AddressBegin (); + iter != AddressEnd (); + iter++) + { + SerializeAddress (mid, iter); + start.Write (mid + headlen, GetAddressLength () - headlen - taillen); + } + } + + flags |= GetPrefixFlags (); + bufref.WriteU8 (flags); + + for (ConstPrefixIterator iter = PrefixBegin (); + iter != PrefixEnd (); + iter++) + { + start.WriteU8 (*iter); + } + } + + m_addressTlvList.Serialize (start); +} + +void +PbbAddressBlock::Deserialize (Buffer::Iterator &start) +{ + uint8_t numaddr = start.ReadU8 (); + uint8_t flags = start.ReadU8 (); + + if (numaddr > 0) + { + uint8_t headlen = 0; + uint8_t taillen = 0; + uint8_t addrtmp[GetAddressLength ()]; + memset(addrtmp, 0, GetAddressLength ()); + + if (flags & AHAS_HEAD) + { + headlen = start.ReadU8 (); + start.Read (addrtmp, headlen); + } + + if ((flags & AHAS_FULL_TAIL) ^ (flags & AHAS_ZERO_TAIL)) + { + taillen = start.ReadU8 (); + + if (flags & AHAS_FULL_TAIL) + { + start.Read (addrtmp + GetAddressLength () - taillen, taillen); + } + } + + for (int i = 0; i < numaddr; i++) + { + start.Read (addrtmp + headlen, GetAddressLength () - headlen - taillen); + AddressPushBack (DeserializeAddress (addrtmp)); + } + + if (flags & AHAS_SINGLE_PRE_LEN) + { + PrefixPushBack (start.ReadU8 ()); + } + else if (flags & AHAS_MULTI_PRE_LEN) + { + for (int i = 0; i < numaddr; i++) + { + PrefixPushBack (start.ReadU8 ()); + } + } + } + + m_addressTlvList.Deserialize (start); +} + +void +PbbAddressBlock::Print (std::ostream &os) const +{ + Print (os, 0); +} + +void +PbbAddressBlock::Print (std::ostream &os, int level) const +{ + std::string prefix = ""; + for (int i = 0; i < level; i++) + { + prefix.append ("\t"); + } + + os << prefix << "PbbAddressBlock {" << std::endl; + os << prefix << "\taddresses = " << std::endl; + for (ConstAddressIterator iter = AddressBegin (); + iter != AddressEnd (); + iter++) + { + os << prefix << "\t\t"; + PrintAddress(os, iter); + os << std::endl; + } + + os << prefix << "\tprefixes = " << std::endl; + for (ConstPrefixIterator iter = PrefixBegin (); + iter != PrefixEnd (); + iter++) + { + os << prefix << "\t\t" << (int)(*iter) << std::endl; + } + + m_addressTlvList.Print (os, level+1); +} + +bool +PbbAddressBlock::operator== (const PbbAddressBlock &other) const +{ + if (AddressSize () != other.AddressSize ()) + { + return false; + } + + ConstAddressIterator tai, oai; + for (tai = AddressBegin (), oai = other.AddressBegin (); + tai != AddressEnd () && oai != other.AddressEnd (); + tai++, oai++) + { + if (*tai != *oai) + { + return false; + } + } + + if (PrefixSize () != other.PrefixSize ()) + { + return false; + } + + ConstPrefixIterator tpi, opi; + for (tpi = PrefixBegin (), opi = other.PrefixBegin (); + tpi != PrefixEnd () && opi != other.PrefixEnd (); + tpi++, opi++) + { + if (*tpi != *opi) + { + return false; + } + } + + if (m_addressTlvList != other.m_addressTlvList) + { + return false; + } + + return true; +} + +bool +PbbAddressBlock::operator!= (const PbbAddressBlock &other) const +{ + return !(*this == other); +} + +uint8_t +PbbAddressBlock::GetPrefixFlags (void) const +{ + switch (PrefixSize ()) + { + case 0: + return 0; + break; + case 1: + return AHAS_SINGLE_PRE_LEN; + break; + default: + return AHAS_MULTI_PRE_LEN; + break; + } + + /* Quiet compiler */ + return 0; +} + +void +PbbAddressBlock::GetHeadTail (uint8_t *head, uint8_t &headlen, + uint8_t *tail, uint8_t &taillen) const +{ + headlen = GetAddressLength (); + taillen = headlen; + + /* Temporary automatic buffers to store serialized addresses */ + uint8_t * buflast = new uint8_t[GetAddressLength ()]; + uint8_t * bufcur = new uint8_t[GetAddressLength ()]; + uint8_t * tmp; + + SerializeAddress (buflast, AddressBegin ()); + + /* Skip the first item */ + for (PbbAddressBlock::ConstAddressIterator iter = AddressBegin ()++; + iter != AddressEnd (); + iter++) + { + SerializeAddress (bufcur, iter); + + int i; + for (i = 0; i < headlen; i++) + { + if (buflast[i] != bufcur[i]) + { + headlen = i; + break; + } + } + + /* If headlen == fulllen - 1, then tail is 0 */ + if (headlen <= GetAddressLength () - 1) + { + for (i = GetAddressLength () - 1; + GetAddressLength () - 1 - i <= taillen && i > headlen; + i--) + { + if (buflast[i] != bufcur[i]) + { + break; + } + } + taillen = GetAddressLength () - 1 - i; + } + else if (headlen == 0) + { + taillen = 0; + break; + } + + tmp = buflast; + buflast = bufcur; + bufcur = tmp; + } + + memcpy(head, bufcur, headlen); + memcpy(tail, bufcur + (GetAddressLength () - taillen), taillen); + + delete[] buflast; + delete[] bufcur; +} + +bool +PbbAddressBlock::HasZeroTail (const uint8_t *tail, uint8_t taillen) const +{ + int i; + for (i = 0; i < taillen; i++) + { + if (tail[i] != 0) + { + break; + } + } + return i == taillen; +} + +/* End PbbAddressBlock Class */ + +PbbAddressBlockIpv4::PbbAddressBlockIpv4 () +{ +} + +PbbAddressBlockIpv4::~PbbAddressBlockIpv4 () +{ +} + +uint8_t +PbbAddressBlockIpv4::GetAddressLength (void) const +{ + return 4; +} + +void +PbbAddressBlockIpv4::SerializeAddress (uint8_t *buffer, ConstAddressIterator iter) const +{ + Ipv4Address::ConvertFrom (*iter).Serialize (buffer); +} + +Address +PbbAddressBlockIpv4::DeserializeAddress (uint8_t *buffer) const +{ + return Ipv4Address::Deserialize (buffer); +} + +void +PbbAddressBlockIpv4::PrintAddress (std::ostream &os, ConstAddressIterator iter) const +{ + Ipv4Address::ConvertFrom (*iter).Print (os); +} + +/* End PbbAddressBlockIpv4 Class */ + +PbbAddressBlockIpv6::PbbAddressBlockIpv6 () +{ +} + +PbbAddressBlockIpv6::~PbbAddressBlockIpv6 () +{ +} + +uint8_t +PbbAddressBlockIpv6::GetAddressLength (void) const +{ + return 16; +} + +void +PbbAddressBlockIpv6::SerializeAddress (uint8_t *buffer, ConstAddressIterator iter) const +{ + Ipv6Address::ConvertFrom (*iter).Serialize (buffer); +} + +Address +PbbAddressBlockIpv6::DeserializeAddress (uint8_t *buffer) const +{ + return Ipv6Address::Deserialize (buffer); +} + +void +PbbAddressBlockIpv6::PrintAddress (std::ostream &os, ConstAddressIterator iter) const +{ + Ipv6Address::ConvertFrom (*iter).Print (os); +} + +/* End PbbAddressBlockIpv6 Class */ + +PbbTlv::PbbTlv (void) +{ + m_refCount = 1; + m_hasTypeExt = false; + m_hasIndexStart = false; + m_hasIndexStop = false; + m_isMultivalue = false; + m_hasValue = false; +} + +void +PbbTlv::SetType (uint8_t type) +{ + m_type = type; +} + +uint8_t +PbbTlv::GetType (void) const +{ + return m_type; +} + +void +PbbTlv::SetTypeExt (uint8_t typeExt) +{ + m_typeExt = typeExt; + m_hasTypeExt = true; +} + +uint8_t +PbbTlv::GetTypeExt (void) const +{ + NS_ASSERT (HasTypeExt ()); + return m_typeExt; +} + +bool +PbbTlv::HasTypeExt (void) const +{ + return m_hasTypeExt; +} + +void +PbbTlv::SetIndexStart (uint8_t index) +{ + m_indexStart = index; + m_hasIndexStart = true; +} + +uint8_t +PbbTlv::GetIndexStart (void) const +{ + NS_ASSERT (HasIndexStart ()); + return m_indexStart; +} + +bool +PbbTlv::HasIndexStart (void) const +{ + return m_hasIndexStart; +} + +void +PbbTlv::SetIndexStop (uint8_t index) +{ + m_indexStop = index; + m_hasIndexStop = true; +} + +uint8_t +PbbTlv::GetIndexStop (void) const +{ + NS_ASSERT (HasIndexStop ()); + return m_indexStop; +} + +bool +PbbTlv::HasIndexStop (void) const +{ + return m_hasIndexStop; +} + +void +PbbTlv::SetMultivalue (bool isMultivalue) +{ + m_isMultivalue = isMultivalue; +} + +bool +PbbTlv::IsMultivalue (void) const +{ + return m_isMultivalue; +} + +void +PbbTlv::SetValue (Buffer start) +{ + m_hasValue = true; + m_value = start; +} + +void +PbbTlv::SetValue (const uint8_t * buffer, uint32_t size) +{ + Buffer value; + value.AddAtStart (size); + value.Begin ().Write (buffer, size); + SetValue (value); +} + +Buffer +PbbTlv::GetValue (void) const +{ + NS_ASSERT (HasValue ()); + return m_value; +} + +bool +PbbTlv::HasValue (void) const +{ + return m_hasValue; +} + +void +PbbTlv::Ref (void) const +{ + m_refCount++; +} + +void +PbbTlv::Unref (void) const +{ + m_refCount--; + if (m_refCount == 0) + { + delete this; + } +} + +uint32_t +PbbTlv::GetSerializedSize (void) const +{ + /* type + flags */ + uint32_t size = 2; + + if (HasTypeExt ()) + { + size++; + } + + if (HasIndexStart ()) + { + size++; + } + + if (HasIndexStop ()) + { + size++; + } + + if (HasValue ()) + { + if (GetValue ().GetSize () > 255) + { + size += 2; + } + else + { + size++; + } + size += GetValue ().GetSize (); + } + + return size; +} + +void +PbbTlv::Serialize (Buffer::Iterator &start) const +{ + start.WriteU8 (GetType ()); + + Buffer::Iterator bufref = start; + uint8_t flags = 0; + start.Next(); + + if (HasTypeExt()) + { + flags |= THAS_TYPE_EXT; + start.WriteU8 (GetTypeExt ()); + } + + if (HasIndexStart ()) + { + start.WriteU8 (GetIndexStart ()); + + if (HasIndexStop ()) + { + flags |= THAS_MULTI_INDEX; + start.WriteU8 (GetIndexStop ()); + } + else + { + flags |= THAS_SINGLE_INDEX; + } + } + + if (HasValue ()) + { + flags |= THAS_VALUE; + + uint32_t size = GetValue ().GetSize (); + if (size > 255) + { + flags |= THAS_EXT_LEN; + start.WriteHtonU16 (size); + } + else + { + start.WriteU8 (size); + } + + if (IsMultivalue ()) + { + flags |= TIS_MULTIVALUE; + } + + start.Write(GetValue ().Begin (), GetValue ().End ()); + } + + bufref.WriteU8 (flags); +} + +void +PbbTlv::Deserialize (Buffer::Iterator &start) +{ + SetType (start.ReadU8 ()); + + uint8_t flags = start.ReadU8 (); + + if (flags & THAS_TYPE_EXT) + { + SetTypeExt (start.ReadU8 ()); + } + + if (flags & THAS_MULTI_INDEX) + { + SetIndexStart (start.ReadU8 ()); + SetIndexStop (start.ReadU8 ()); + } + else if (flags & THAS_SINGLE_INDEX) + { + SetIndexStart (start.ReadU8 ()); + } + + if (flags & THAS_VALUE) + { + uint16_t len = 0; + + if (flags & THAS_EXT_LEN) + { + len = start.ReadNtohU16 (); + } + else + { + len = start.ReadU8 (); + } + + m_value.AddAtStart (len); + + Buffer::Iterator valueStart = start; + start.Next (len); + m_value.Begin ().Write (valueStart, start); + m_hasValue = true; + } +} + +void +PbbTlv::Print (std::ostream &os) const +{ + Print (os, 0); +} + +void +PbbTlv::Print (std::ostream &os, int level) const +{ + std::string prefix = ""; + for (int i = 0; i < level; i++) + { + prefix.append ("\t"); + } + + os << prefix << "PbbTlv {" << std::endl; + os << prefix << "\ttype = " << (int)GetType () << std::endl; + + if (HasTypeExt ()) + { + os << prefix << "\ttypeext = " << (int)GetTypeExt () << std::endl; + } + + if (HasIndexStart ()) + { + os << prefix << "\tindexStart = " << (int)GetIndexStart () << std::endl; + } + + if (HasIndexStop ()) + { + os << prefix << "\tindexStop = " << (int)GetIndexStop () << std::endl; + } + + os << prefix << "\tisMultivalue = " << IsMultivalue () << std::endl; + + if (HasValue ()) + { + os << prefix << "\thas value; size = " << GetValue (). GetSize () << std::endl; + } + + os << prefix << "}" << std::endl; +} + +bool +PbbTlv::operator== (const PbbTlv &other) const +{ + if (GetType () != other.GetType ()) + { + return false; + } + + if (HasTypeExt () != other.HasTypeExt ()) + { + return false; + } + + if (HasTypeExt ()) + { + if (GetTypeExt () != other.GetTypeExt ()) + { + return false; + } + } + + if (HasValue () != other.HasValue ()) + { + return false; + } + + if (HasValue ()) + { + Buffer tv = GetValue (); + Buffer ov = other.GetValue (); + if (tv.GetSize () != ov.GetSize ()) + { + return false; + } + + /* The docs say I probably shouldn't use Buffer::PeekData, but I think it + * is justified in this case. */ + if (memcmp (tv.PeekData (), ov.PeekData (), tv.GetSize ()) != 0) + { + return false; + } + } + return true; +} + +bool +PbbTlv::operator!= (const PbbTlv &other) const +{ + return !(*this == other); +} + +/* End PbbTlv Class */ + +void +PbbAddressTlv::SetIndexStart (uint8_t index) +{ + PbbTlv::SetIndexStart (index); +} + +uint8_t +PbbAddressTlv::GetIndexStart (void) const +{ + return PbbTlv::GetIndexStart (); +} + +bool +PbbAddressTlv::HasIndexStart (void) const +{ + return PbbTlv::HasIndexStart (); +} + +void +PbbAddressTlv::SetIndexStop (uint8_t index) +{ + PbbTlv::SetIndexStop (index); +} + +uint8_t +PbbAddressTlv::GetIndexStop (void) const +{ + return PbbTlv::GetIndexStop (); +} + +bool +PbbAddressTlv::HasIndexStop (void) const +{ + return PbbTlv::HasIndexStop (); +} + +void +PbbAddressTlv::SetMultivalue (bool isMultivalue) +{ + PbbTlv::SetMultivalue (isMultivalue); +} + +bool +PbbAddressTlv::IsMultivalue (void) const +{ + return PbbTlv::IsMultivalue (); +} + +} /* namespace ns3 */ diff --git a/src/node/packetbb.h b/src/node/packetbb.h new file mode 100644 index 000000000..7cdca5074 --- /dev/null +++ b/src/node/packetbb.h @@ -0,0 +1,1745 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* vim: set ts=2 sw=2 sta expandtab ai si cin: */ +/* + * Copyright (c) 2009 Drexel University + * + * 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: Tom Wambold + */ +/* These classes implement RFC 5444 - The Generalized Mobile Ad Hoc Network + * (MANET) Packet/PbbMessage Format + * See: http://tools.ietf.org/html/rfc5444 for details */ + +#ifndef PACKETBB_H +#define PACKETBB_H + +#include + +#include "ns3/ptr.h" +#include "ns3/address.h" +#include "ns3/header.h" +#include "ns3/buffer.h" + +namespace ns3 { + +/* Forward declare objects */ +class PbbMessage; +class PbbAddressBlock; +class PbbTlv; +class PbbAddressTlv; + +/** Used in Messages to determine whether it contains IPv4 or IPv6 addresses */ +enum PbbAddressLength { + IPV4 = 3, + IPV6 = 15, +}; + +/** + * \brief A block of packet or message TLVs (PbbTlv). + * + * Acts similar to a C++ STL container. Should not be used for Address TLVs. + */ +class PbbTlvBlock +{ +public: + typedef std::list< Ptr >::iterator Iterator; + typedef std::list< Ptr >::const_iterator ConstIterator; + + /** + * \return an iterator to the first TLV in this block. + */ + Iterator Begin (void); + + /** + * \return a const iterator to the first TLV in this block. + */ + ConstIterator Begin (void) const; + + /** + * \return an iterator to the past-the-end element in this block. + */ + Iterator End (void); + + /** + * \return a const iterator to the past-the-end element in this block. + */ + ConstIterator End (void) const; + + /** + * \return the number of TLVs in this block. + */ + int Size (void) const; + + /** + * \return true if there are no TLVs in this block, false otherwise. + */ + bool Empty (void) const; + + /** + * \return a smart pointer to the first TLV in this block. + */ + Ptr Front (void) const; + + /** + * \return a smart pointer to the last TLV in this block. + */ + Ptr Back (void) const; + + /** + * \brief Prepends a TLV to the front of this block. + * \param tlv a smart pointer to the TLV to prepend. + */ + void PushFront (Ptr tlv); + + /** + * \brief Removes a TLV from the front of this block. + */ + void PopFront (void); + + /** + * \brief Appends a TLV to the back of this block. + * \param tlv a smart pointer to the TLV to append. + */ + void PushBack (Ptr tlv); + + /** + * \brief Removes a TLV from the back of this block. + */ + void PopBack (void); + + /** + * \brief Inserts a TLV at the specified position in this block. + * \param position an Iterator pointing to the position in this block to + * insert the TLV. + * \param tlv a smart pointer to the TLV to insert. + * \return An iterator pointing to the newly inserted TLV. + */ + Iterator Insert (Iterator position, const Ptr tlv); + + /** + * \brief Removes the TLV at the specified position. + * \param position an Iterator pointing to the TLV to erase. + * \return an iterator pointing to the next TLV in the block. + */ + Iterator Erase (Iterator position); + + /** + * \brief Removes all TLVs from [first, last) (includes first, not includes + * last). + * \param first an Iterator pointing to the first TLV to erase (inclusive). + * \param last an Iterator pointing to the element past the last TLV to erase. + * \return an iterator pointing to the next TLV in the block. + */ + Iterator Erase (Iterator first, Iterator last); + + /** + * \brief Removes all TLVs from this block. + */ + void Clear (void); + + /** + * \return The size (in bytes) needed to serialize this block. + */ + uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this block into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + * + * Users should not need to call this. Blocks will be serialized by their + * containing packet. + */ + void Serialize (Buffer::Iterator &start) const; + + /** + * \brief Deserializes a block from the specified buffer. + * \param start a reference to the point in a buffer to begin deserializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Deserialize (Buffer::Iterator &start); + + /** + * \brief Pretty-prints the contents of this block. + * \param os a stream object to print to. + */ + void Print (std::ostream &os) const; + + /** + * \brief Pretty-prints the contents of this block, with specified indentation. + * \param os a stream object to print to. + * \param level level of indentation. + * + * This probably never needs to be called by users. This is used when + * recursively printing sub-objects. + */ + void Print (std::ostream &os, int level) const; + + bool operator== (const PbbTlvBlock &other) const; + bool operator!= (const PbbTlvBlock &other) const; + +private: + std::list< Ptr > m_tlvList; +}; + +/** + * \brief A block of Address TLVs (PbbAddressTlv). + * + * Acts similar to a C++ STL container. + */ +class PbbAddressTlvBlock +{ +public: + typedef std::list< Ptr >::iterator Iterator; + typedef std::list< Ptr >::const_iterator ConstIterator; + + /** + * \return an iterator to the first Address TLV in this block. + */ + Iterator Begin (void); + + /** + * \return a const iterator to the first Address TLV in this block. + */ + ConstIterator Begin (void) const; + + /** + * \return an iterator to the past-the-end element in this block. + */ + Iterator End (void); + + /** + * \return a const iterator to the past-the-end element in this block. + */ + ConstIterator End (void) const; + + /** + * \return the number of Address TLVs in this block. + */ + int Size (void) const; + + /** + * \return true if there are no Address TLVs in this block, false otherwise. + */ + bool Empty (void) const; + + /** + * \return the first Address TLV in this block. + */ + Ptr Front (void) const; + + /** + * \return the last AddressTLV in this block. + */ + Ptr Back (void) const; + + /** + * \brief Prepends an Address TLV to the front of this block. + * \param tlv a smart pointer to the Address TLV to prepend. + */ + void PushFront (Ptr tlv); + + /** + * \brief Removes an AddressTLV from the front of this block. + */ + void PopFront (void); + + /** + * \brief Appends an Address TLV to the back of this block. + * \param tlv a smart pointer to the Address TLV to append. + */ + void PushBack (Ptr tlv); + + /** + * \brief Removes an Address TLV from the back of this block. + */ + void PopBack (void); + + /** + * \brief Inserts an Address TLV at the specified position in this block. + * \param position an Iterator pointing to the position in this block to + * insert the Address TLV. + * \param tlv a smart pointer to the Address TLV to insert. + * \return An iterator pointing to the newly inserted Address TLV. + */ + Iterator Insert (Iterator position, const Ptr tlv); + + /** + * \brief Removes the Address TLV at the specified position. + * \param position an Iterator pointing to the Address TLV to erase. + * \return an iterator pointing to the next Address TLV in the block. + */ + Iterator Erase (Iterator position); + + /** + * \brief Removes all Address TLVs from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first Address TLV to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last Address TLV + * to erase. + * \return an iterator pointing to the next Address TLV in the block. + */ + Iterator Erase (Iterator first, Iterator last); + + /** + * \brief Removes all Address TLVs from this block. + */ + void Clear (void); + + /** + * \return The size (in bytes) needed to serialize this block. + */ + uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this block into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + * + * Users should not need to call this. Blocks will be serialized by their + * containing packet. + */ + void Serialize (Buffer::Iterator &start) const; + + /** + * \brief Deserializes a block from the specified buffer. + * \param start a reference to the point in a buffer to begin deserializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Deserialize (Buffer::Iterator &start); + + /** + * \brief Pretty-prints the contents of this block. + * \param os a stream object to print to. + */ + void Print (std::ostream &os) const; + + /** + * \brief Pretty-prints the contents of this block, with specified indentation. + * \param os a stream object to print to. + * \param level level of indentation. + * + * This probably never needs to be called by users. This is used when + * recursively printing sub-objects. + */ + void Print (std::ostream &os, int level) const; + + bool operator== (const PbbAddressTlvBlock &other) const; + bool operator!= (const PbbAddressTlvBlock &other) const; + +private: + std::list< Ptr > m_tlvList; +}; + +/** + * \brief Main PacketBB Packet object. + * + * A PacketBB packet is made up of zero or more packet TLVs (PbbTlv), and zero + * or more messages (PbbMessage). + * + * See: http://tools.ietf.org/html/rfc5444 for details. + */ +class PbbPacket : public Header +{ +public: + typedef std::list< Ptr >::iterator TlvIterator; + typedef std::list< Ptr >::const_iterator ConstTlvIterator; + typedef std::list< Ptr >::iterator MessageIterator; + typedef std::list< Ptr >::const_iterator ConstMessageIterator; + + PbbPacket (void); + + /** + * \return the version of PacketBB that constructed this packet. + * + * This will always return 0 for packets constructed using this API. + */ + uint8_t GetVersion (void) const; + + /** + * \brief Sets the sequence number of this packet. + * \param number the sequence number. + */ + void SetSequenceNumber (uint16_t number); + + /** + * \return the sequence number of this packet. + * + * Calling this while HasSequenceNumber is False is undefined. Make sure you + * check it first. This will be checked by an assert in debug builds. + */ + uint16_t GetSequenceNumber (void) const; + + /** + * \brief Tests whether or not this packet has a sequence number. + * \return true if this packet has a sequence number, false otherwise. + * + * This should be called before calling GetSequenceNumber to make sure there + * actually is one. + */ + bool HasSequenceNumber (void) const; + + /* Manipulating Packet TLVs */ + + /** + * \return an iterator to the first Packet TLV in this packet. + */ + TlvIterator TlvBegin (void); + + /** + * \return a const iterator to the first Packet TLV in this packet. + */ + ConstTlvIterator TlvBegin (void) const; + + /** + * \return an iterator to the past-the-end element in this packet TLV block. + */ + TlvIterator TlvEnd (void); + + /** + * \return a const iterator to the past-the-end element in this packet TLV + * block. + */ + ConstTlvIterator TlvEnd (void) const; + + /** + * \return the number of packet TLVs in this packet. + */ + int TlvSize (void) const; + + /** + * \return true if there are no packet TLVs in this packet, false otherwise. + */ + bool TlvEmpty (void) const; + + /** + * \return a smart pointer to the first packet TLV in this packet. + */ + Ptr TlvFront (void); + + /** + * \return a const smart pointer to the first packet TLV in this packet. + */ + const Ptr TlvFront (void) const; + + /** + * \return a smart pointer to the last packet TLV in this packet. + */ + Ptr TlvBack (void); + + /** + * \return a const smart pointer to the last packet TLV in this packet. + */ + const Ptr TlvBack (void) const; + + /** + * \brief Prepends a packet TLV to the front of this packet. + * \param tlv a smart pointer to the packet TLV to prepend. + */ + void TlvPushFront (Ptr tlv); + + /** + * \brief Removes a packet TLV from the front of this packet. + */ + void TlvPopFront (void); + + /** + * \brief Appends a packet TLV to the back of this packet. + * \param tlv a smart pointer to the packet TLV to append. + */ + void TlvPushBack (Ptr tlv); + + /** + * \brief Removes a packet TLV from the back of this block. + */ + void TlvPopBack (void); + + /** + * \brief Removes the packet TLV at the specified position. + * \param position an Iterator pointing to the packet TLV to erase. + * \return an iterator pointing to the next packet TLV in the block. + */ + TlvIterator Erase (TlvIterator position); + + /** + * \brief Removes all packet TLVs from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first packet TLV to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last packet TLV + * to erase. + * \return an iterator pointing to the next packet TLV in the block. + */ + TlvIterator Erase (TlvIterator first, TlvIterator last); + + /** + * \brief Removes all packet TLVs from this packet. + */ + void TlvClear (void); + + /* Manipulating Packet Messages */ + + /** + * \return an iterator to the first message in this packet. + */ + MessageIterator MessageBegin (void); + + /** + * \return a const iterator to the first message in this packet. + */ + ConstMessageIterator MessageBegin (void) const; + + /** + * \return an iterator to the past-the-end element in this message block. + */ + MessageIterator MessageEnd (void); + + /** + * \return a const iterator to the past-the-end element in this message + * block. + */ + ConstMessageIterator MessageEnd (void) const; + + /** + * \return the number of messages in this packet. + */ + int MessageSize (void) const; + + /** + * \return true if there are no messages in this packet, false otherwise. + */ + bool MessageEmpty (void) const; + + /** + * \return a smart pointer to the first message in this packet. + */ + Ptr MessageFront (void); + + /** + * \return a cosnt smart pointer to the first message in this packet. + */ + const Ptr MessageFront (void) const; + + /** + * \return a smart pointer to the last message in this packet. + */ + Ptr MessageBack (void); + + /** + * \return a cosnt smart pointer to the last message in this packet. + */ + const Ptr MessageBack (void) const; + + /** + * \brief Prepends a message to the front of this packet. + * \param message a smart pointer to the message to prepend. + */ + void MessagePushFront (Ptr message); + + /** + * \brief Removes a message from the front of this packet. + */ + void MessagePopFront (void); + + /** + * \brief Appends a message to the back of this packet. + * \param message a smart pointer to the message to append. + */ + void MessagePushBack (Ptr message); + + /** + * \brief Removes a message from the back of this packet. + */ + void MessagePopBack (void); + + /** + * \brief Removes the message at the specified position. + * \param position an Iterator pointing to the message to erase. + * \return an iterator pointing to the next message in the packet. + */ + MessageIterator Erase (MessageIterator position); + + /** + * \brief Removes all messages from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first message to erase (inclusive). + * \param last an Iterator pointing to the element past the last message to erase. + * \return an iterator pointing to the next message in the block. + */ + MessageIterator Erase (MessageIterator first, MessageIterator last); + + /** + * \brief Removes all messages from this packet. + */ + void MessageClear (void); + + /* Smart pointer methods */ + void Ref (void) const; + void Unref (void) const; + + /* Methods implemented by all headers */ + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + /** + * \return The size (in bytes) needed to serialize this packet. + */ + virtual uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this packet into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + */ + virtual void Serialize (Buffer::Iterator start) const; + + /** + * \brief Deserializes a packet from the specified buffer. + * \return the number of bytes deserialized + * + * If this returns a number smaller than the total number of bytes in the + * buffer, there was an error. + */ + virtual uint32_t Deserialize (Buffer::Iterator start); + + /** + * \brief Pretty-prints the contents of this block. + * \param os a stream object to print to. + */ + virtual void Print (std::ostream &os) const; + + bool operator== (const PbbPacket &other) const; + bool operator!= (const PbbPacket &other) const; + +protected: + void SerializePacketTlv (Buffer::Iterator &start) const; + +private: + PbbTlvBlock m_tlvList; + std::list< Ptr > m_messageList; + + uint8_t m_version; + + bool m_hasseqnum; + uint16_t m_seqnum; + + mutable uint32_t m_refCount; +}; + +/** + * \brief A message within a PbbPacket packet. + * + * There may be any number of messages in one packet packet. This is a pure + * virtual base class, when creating a message, you should instantiate either + * PbbMessageIpv4 or PbbMessageIpv6. + */ +class PbbMessage +{ +public: + typedef std::list< Ptr >::iterator TlvIterator; + typedef std::list< Ptr >::const_iterator ConstTlvIterator; + typedef std::list< Ptr >::iterator AddressBlockIterator; + typedef std::list< Ptr >::const_iterator ConstAddressBlockIterator; + + PbbMessage (); + virtual ~PbbMessage (); + + /** + * \brief Sets the type for this message. + * \param type the type to set. + */ + void SetType (uint8_t type); + + /** + * \return the type assigned to this packet + */ + uint8_t GetType (void) const; + + /** + * \brief Sets the address for the node that created this packet. + * \param address the originator address. + */ + void SetOriginatorAddress (Address address); + + /** + * \return the address of the node that created this packet. + * + * Calling this while HasOriginatorAddress is False is undefined. Make sure + * you check it first. This will be checked by an assert in debug builds. + */ + Address GetOriginatorAddress (void) const; + + /** + * \brief Tests whether or not this message has an originator address. + * \return true if this message has an originator address, false otherwise. + */ + bool HasOriginatorAddress (void) const; + + /** + * \brief Sets the maximum number of hops this message should travel + * \param hoplimit the limit to set + */ + void SetHopLimit (uint8_t hoplimit); + + /** + * \return the maximum number of hops this message should travel. + * + * Calling this while HasHopLimit is False is undefined. Make sure you check + * it first. This will be checked by an assert in debug builds. + */ + uint8_t GetHopLimit (void) const; + + /** + * \brief Tests whether or not this message has a hop limit. + * \return true if this message has a hop limit, false otherwise. + * + * If this is set, messages should not hop further than this limit. + */ + bool HasHopLimit (void) const; + + /** + * \brief Sets the current number of hops this message has traveled. + * \param hopcount the current number of hops + */ + void SetHopCount (uint8_t hopcount); + + /** + * \return the current number of hops this message has traveled. + * + * Calling this while HasHopCount is False is undefined. Make sure you check + * it first. This will be checked by an assert in debug builds. + */ + uint8_t GetHopCount (void) const; + + /** + * \brief Tests whether or not this message has a hop count. + * \return true if this message has a hop limit, false otherwise. + */ + bool HasHopCount (void) const; + + /** + * \brief Sets the sequence number of this message. + * \param seqnum the sequence number to set. + */ + void SetSequenceNumber (uint16_t seqnum); + + /** + * \return the sequence number of this message. + * + * Calling this while HasSequenceNumber is False is undefined. Make sure you + * check it first. This will be checked by an assert in debug builds. + */ + uint16_t GetSequenceNumber (void) const; + + /** + * \brief Tests whether or not this message has a sequence number. + * \return true if this message has a sequence number, false otherwise. + */ + bool HasSequenceNumber (void) const; + + /* Manipulating PbbMessage TLVs */ + + /** + * \return an iterator to the first message TLV in this message. + */ + TlvIterator TlvBegin (); + + /** + * \return a const iterator to the first message TLV in this message. + */ + ConstTlvIterator TlvBegin () const; + + /** + * \return an iterator to the past-the-end message TLV element in this + * message. + */ + TlvIterator TlvEnd (); + + /** + * \return a const iterator to the past-the-end message TLV element in this + * message. + */ + ConstTlvIterator TlvEnd () const; + + /** + * \return the number of message TLVs in this message. + */ + int TlvSize (void) const; + + /** + * \return true if there are no message TLVs in this message, false otherwise. + */ + bool TlvEmpty (void) const; + + /** + * \return a smart pointer to the first message TLV in this message. + */ + Ptr TlvFront (void); + + /** + * \return a const smart pointer to the first message TLV in this message. + */ + const Ptr TlvFront (void) const; + + /** + * \return a smart pointer to the last message TLV in this message. + */ + Ptr TlvBack (void); + + /** + * \return a const smart pointer to the last message TLV in this message. + */ + const Ptr TlvBack (void) const; + + /** + * \brief Prepends a message TLV to the front of this message. + * \param tlv a smart pointer to the message TLV to prepend. + */ + void TlvPushFront (Ptr tlv); + + /** + * \brief Removes a message TLV from the front of this message. + */ + void TlvPopFront (void); + + /** + * \brief Appends a message TLV to the back of this message. + * \param tlv a smart pointer to the message TLV to append. + */ + void TlvPushBack (Ptr tlv); + + /** + * \brief Removes a message TLV from the back of this message. + */ + void TlvPopBack (void); + + /** + * \brief Removes the message TLV at the specified position. + * \param position an Iterator pointing to the message TLV to erase. + * \return an iterator pointing to the next TLV in the block. + */ + TlvIterator TlvErase (TlvIterator position); + + /** + * \brief Removes all message TLVs from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first message TLV to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last message TLV + * to erase. + * \return an iterator pointing to the next message TLV in the message. + */ + TlvIterator TlvErase (TlvIterator first, TlvIterator last); + + /** + * \brief Removes all message TLVs from this block. + */ + void TlvClear (void); + + /* Manipulating Address Block and Address TLV pairs */ + + /** + * \return an iterator to the first address block in this message. + */ + AddressBlockIterator AddressBlockBegin (); + + /** + * \return a const iterator to the first address block in this message. + */ + ConstAddressBlockIterator AddressBlockBegin () const; + + /** + * \return an iterator to the past-the-end address block element in this + * message. + */ + AddressBlockIterator AddressBlockEnd (); + + /** + * \return a const iterator to the past-the-end address block element in this + * message. + */ + ConstAddressBlockIterator AddressBlockEnd () const; + + /** + * \return the number of address blocks in this message. + */ + int AddressBlockSize (void) const; + + /** + * \return true if there are no address blocks in this message, false + * otherwise. + */ + bool AddressBlockEmpty (void) const; + + /** + * \return a smart pointer to the first address block in this message. + */ + Ptr AddressBlockFront (void); + + /** + * \return a const smart pointer to the first address block in this message. + */ + const Ptr AddressBlockFront (void) const; + + /** + * \return a smart pointer to the last address block in this message. + */ + Ptr AddressBlockBack (void); + + /** + * \return a const smart pointer to the last address block in this message. + */ + const Ptr AddressBlockBack (void) const; + + /** + * \brief Prepends an address block to the front of this message. + * \param block a smart pointer to the address block to prepend. + */ + void AddressBlockPushFront (Ptr block); + + /** + * \brief Removes an address block from the front of this message. + */ + void AddressBlockPopFront (void); + + /** + * \brief Appends an address block to the front of this message. + * \param block a smart pointer to the address block to append. + */ + void AddressBlockPushBack (Ptr block); + + /** + * \brief Removes an address block from the back of this message. + */ + void AddressBlockPopBack (void); + + /** + * \brief Removes the address block at the specified position. + * \param position an Iterator pointing to the address block to erase. + * \return an iterator pointing to the next address block in the message. + */ + AddressBlockIterator AddressBlockErase (AddressBlockIterator position); + + /** + * \brief Removes all address blocks from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first address block to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last address + * block to erase. + * \return an iterator pointing to the next address block in the message. + */ + AddressBlockIterator AddressBlockErase (AddressBlockIterator first, + AddressBlockIterator last); + + /** + * \brief Removes all address blocks from this message. + */ + void AddressBlockClear (void); + + /* Smart pointer methods */ + void Ref (void) const; + void Unref (void) const; + + /** + * \brief Deserializes a message, returning the correct object depending on + * whether it is an IPv4 message or an IPv6 message. + * \param start a reference to the point in a buffer to begin deserializing. + * \return A pointer to the deserialized message, or 0 on error. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + static Ptr DeserializeMessage (Buffer::Iterator &start); + + /** + * \return The size (in bytes) needed to serialize this message. + */ + uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this message into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Serialize (Buffer::Iterator &start) const; + + /** + * \brief Deserializes a message from the specified buffer. + * \param start a reference to the point in a buffer to begin deserializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Deserialize (Buffer::Iterator &start); + + /** + * \brief Pretty-prints the contents of this message. + * \param os a stream object to print to. + */ + void Print (std::ostream &os) const; + + /** + * \brief Pretty-prints the contents of this message, with specified + * indentation. + * \param os a stream object to print to. + * \param level level of indentation. + * + * This probably never needs to be called by users. This is used when + * recursively printing sub-objects. + */ + void Print (std::ostream &os, int level) const; + + bool operator== (const PbbMessage &other) const; + bool operator!= (const PbbMessage &other) const; + +protected: + /* PbbMessage size in bytes - 1. + * + * IPv4 = 4 - 1 = 3, IPv6 = 16 - 1 = 15 + */ + virtual PbbAddressLength GetAddressLength (void) const = 0; + + virtual void SerializeOriginatorAddress (Buffer::Iterator &start) const = 0; + virtual Address DeserializeOriginatorAddress (Buffer::Iterator &start) const = 0; + virtual void PrintOriginatorAddress (std::ostream &os) const = 0; + + virtual Ptr AddressBlockDeserialize (Buffer::Iterator &start) const = 0; + +private: + PbbTlvBlock m_tlvList; + std::list< Ptr > m_addressBlockList; + + uint8_t m_type; + PbbAddressLength m_addrSize; + + bool m_hasOriginatorAddress; + Address m_originatorAddress; + + bool m_hasHopLimit; + uint8_t m_hopLimit; + + bool m_hasHopCount; + uint8_t m_hopCount; + + bool m_hasSequenceNumber; + uint16_t m_sequenceNumber; + + mutable uint32_t m_refCount; +}; + +/** + * \brief Concrete IPv4 specific PbbMessage. + * + * This message will only contain IPv4 addresses. + */ +class PbbMessageIpv4 : public PbbMessage { +public: + PbbMessageIpv4 (); + virtual ~PbbMessageIpv4 (); + +protected: + virtual PbbAddressLength GetAddressLength (void) const; + + virtual void SerializeOriginatorAddress (Buffer::Iterator &start) const; + virtual Address DeserializeOriginatorAddress (Buffer::Iterator &start) const; + virtual void PrintOriginatorAddress (std::ostream &os) const; + + virtual Ptr AddressBlockDeserialize (Buffer::Iterator &start) const; +}; + +/** + * \brief Concrete IPv6 specific PbbMessage class. + * + * This message will only contain IPv6 addresses. + */ +class PbbMessageIpv6 : public PbbMessage { +public: + PbbMessageIpv6 (); + virtual ~PbbMessageIpv6 (); + +protected: + virtual PbbAddressLength GetAddressLength (void) const; + + virtual void SerializeOriginatorAddress (Buffer::Iterator &start) const; + virtual Address DeserializeOriginatorAddress (Buffer::Iterator &start) const; + virtual void PrintOriginatorAddress (std::ostream &os) const; + + virtual Ptr AddressBlockDeserialize (Buffer::Iterator &start) const; +}; + +/** + * \brief An Address Block and its associated Address TLV Blocks. + * + * This is a pure virtual base class, when creating address blocks, you should + * instantiate either PbbAddressBlockIpv4 or PbbAddressBlockIpv6. + */ +class PbbAddressBlock +{ +public: + typedef std::list< Address >::iterator AddressIterator; + typedef std::list< Address >::const_iterator ConstAddressIterator; + + typedef std::list::iterator PrefixIterator; + typedef std::list::const_iterator ConstPrefixIterator; + + typedef PbbAddressTlvBlock::Iterator TlvIterator; + typedef PbbAddressTlvBlock::ConstIterator ConstTlvIterator; + + PbbAddressBlock (); + virtual ~PbbAddressBlock (); + + /* Manipulating the address block */ + + /** + * \return an iterator to the first address in this block. + */ + AddressIterator AddressBegin (void); + + /** + * \return a const iterator to the first address in this block. + */ + ConstAddressIterator AddressBegin (void) const; + + /** + * \return an iterator to the last address in this block. + */ + AddressIterator AddressEnd (void); + + /** + * \return a const iterator to the last address in this block. + */ + ConstAddressIterator AddressEnd (void) const; + + /** + * \return the number of addresses in this block. + */ + int AddressSize (void) const; + + /** + * \return true if there are no addresses in this block, false otherwise. + */ + bool AddressEmpty (void) const; + + /** + * \return the first address in this block. + */ + Address AddressFront (void) const; + + /** + * \return the last address in this block. + */ + Address AddressBack (void) const; + + /** + * \brief Prepends an address to the front of this block. + * \param address the address to prepend. + */ + void AddressPushFront (Address address); + + /** + * \brief Removes an address from the front of this block. + */ + void AddressPopFront (void); + + /** + * \brief Appends an address to the back of this block. + * \param address the address to append. + */ + void AddressPushBack (Address address); + + /** + * \brief Removes an address from the back of this block. + */ + void AddressPopBack (void); + + /** + * \brief Inserts an address at the specified position in this block. + * \param position an Iterator pointing to the position in this block to + * insert the address. + * \param value the address to insert. + * \return An iterator pointing to the newly inserted address. + */ + AddressIterator AddressInsert (AddressIterator position, + const Address value); + + /** + * \brief Removes the address at the specified position. + * \param position an Iterator pointing to the address to erase. + * \return an iterator pointing to the next address in the block. + */ + AddressIterator AddressErase (AddressIterator position); + + /** + * \brief Removes all addresses from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first address to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last address to + * erase. + * \return an iterator pointing to the next address in the block. + */ + AddressIterator AddressErase (AddressIterator first, AddressIterator last); + + /** + * \brief Removes all addresses from this block. + */ + void AddressClear (void); + + /* Prefix methods */ + + /** + * \return an iterator to the first prefix in this block. + */ + PrefixIterator PrefixBegin (void); + + /** + * \return a const iterator to the first prefix in this block. + */ + ConstPrefixIterator PrefixBegin (void) const; + + /** + * \return an iterator to the last prefix in this block. + */ + PrefixIterator PrefixEnd (void); + + /** + * \return a const iterator to the last prefix in this block. + */ + ConstPrefixIterator PrefixEnd (void) const; + + /** + * \return the number of prefixes in this block. + */ + int PrefixSize (void) const; + + /** + * \return true if there are no prefixes in this block, false otherwise. + */ + bool PrefixEmpty (void) const; + + /** + * \return the first prefix in this block. + */ + uint8_t PrefixFront (void) const; + + /** + * \return the last prefix in this block. + */ + uint8_t PrefixBack (void) const; + + /** + * \brief Prepends a prefix to the front of this block. + * \param prefix the prefix to prepend. + */ + void PrefixPushFront (uint8_t prefix); + + /** + * \brief Removes a prefix from the front of this block. + */ + void PrefixPopFront (void); + + /** + * \brief Appends a prefix to the back of this block. + * \param prefix the prefix to append. + */ + void PrefixPushBack (uint8_t prefix); + + /** + * \brief Removes a prefix from the back of this block. + */ + void PrefixPopBack (void); + + /** + * \brief Inserts a prefix at the specified position in this block. + * \param position an Iterator pointing to the position in this block to + * insert the prefix. + * \param value the prefix to insert. + * \return An iterator pointing to the newly inserted prefix. + */ + PrefixIterator PrefixInsert (PrefixIterator position, const uint8_t value); + + /** + * \brief Removes the prefix at the specified position. + * \param position an Iterator pointing to the prefix to erase. + * \return an iterator pointing to the next prefix in the block. + */ + PrefixIterator PrefixErase (PrefixIterator position); + + /** + * \brief Removes all prefixes from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first prefix to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last prefix to + * erase. + * \return an iterator pointing to the next prefix in the block. + */ + PrefixIterator PrefixErase (PrefixIterator first, PrefixIterator last); + + /** + * \brief Removes all prefixes from this block. + */ + void PrefixClear (void); + + /* Manipulating the TLV block */ + + /** + * \return an iterator to the first address TLV in this block. + */ + TlvIterator TlvBegin (void); + + /** + * \return a const iterator to the first address TLV in this block. + */ + ConstTlvIterator TlvBegin (void) const; + + /** + * \return an iterator to the last address TLV in this block. + */ + TlvIterator TlvEnd (void); + + /** + * \return a const iterator to the last address TLV in this block. + */ + ConstTlvIterator TlvEnd (void) const; + + /** + * \return the number of address TLVs in this block. + */ + int TlvSize (void) const; + + /** + * \return true if there are no address TLVs in this block, false otherwise. + */ + bool TlvEmpty (void) const; + + /** + * \return a smart pointer to the first address TLV in this block. + */ + Ptr TlvFront (void); + + /** + * \return a const smart pointer to the first address TLV in this message. + */ + const Ptr TlvFront (void) const; + + /** + * \return a smart pointer to the last address TLV in this message. + */ + Ptr TlvBack (void); + + /** + * \return a const smart pointer to the last address TLV in this message. + */ + const Ptr TlvBack (void) const; + + /** + * \brief Prepends an address TLV to the front of this message. + * \param address a smart pointer to the address TLV to prepend. + */ + void TlvPushFront (Ptr address); + + /** + * \brief Removes an address TLV from the front of this message. + */ + void TlvPopFront (void); + + /** + * \brief Appends an address TLV to the back of this message. + * \param address a smart pointer to the address TLV to append. + */ + void TlvPushBack (Ptr address); + + /** + * \brief Removes an address TLV from the back of this message. + */ + void TlvPopBack (void); + + /** + * \brief Inserts an address TLV at the specified position in this block. + * \param position an Iterator pointing to the position in this block to + * insert the address TLV. + * \param value the prefix to insert. + * \return An iterator pointing to the newly inserted address TLV. + */ + TlvIterator TlvInsert (TlvIterator position, const Ptr value); + + /** + * \brief Removes the address TLV at the specified position. + * \param position an Iterator pointing to the address TLV to erase. + * \return an iterator pointing to the next address TLV in the block. + */ + TlvIterator TlvErase (TlvIterator position); + + /** + * \brief Removes all address TLVs from [first, last) (includes first, not + * includes last). + * \param first an Iterator pointing to the first address TLV to erase + * (inclusive). + * \param last an Iterator pointing to the element past the last address TLV + * to erase. + * \return an iterator pointing to the next address TLV in the message. + */ + TlvIterator TlvErase (TlvIterator first, TlvIterator last); + + /** + * \brief Removes all address TLVs from this block. + */ + void TlvClear (void); + + /* Smart pointer methods */ + void Ref (void) const; + void Unref (void) const; + + /** + * \return The size (in bytes) needed to serialize this address block. + */ + uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this address block into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Serialize (Buffer::Iterator &start) const; + + /** + * \brief Deserializes an address block from the specified buffer. + * \param start a reference to the point in a buffer to begin deserializing. + * + * Users should not need to call this. Blocks will be deserialized by their + * containing packet. + */ + void Deserialize (Buffer::Iterator &start); + + /** + * \brief Pretty-prints the contents of this address block. + * \param os a stream object to print to. + */ + void Print (std::ostream &os) const; + + /** + * \brief Pretty-prints the contents of this address block, with specified + * indentation. + * \param os a stream object to print to. + * \param level level of indentation. + * + * This probably never needs to be called by users. This is used when + * recursively printing sub-objects. + */ + void Print (std::ostream &os, int level) const; + + bool operator== (const PbbAddressBlock &other) const; + bool operator!= (const PbbAddressBlock &other) const; + +protected: + virtual uint8_t GetAddressLength (void) const = 0; + + virtual void SerializeAddress (uint8_t *buffer, ConstAddressIterator iter) const = 0; + virtual Address DeserializeAddress (uint8_t *buffer) const = 0; + virtual void PrintAddress (std::ostream &os, ConstAddressIterator iter) const = 0; + +private: + uint8_t GetPrefixFlags (void) const; + void GetHeadTail (uint8_t *head, uint8_t &headlen, + uint8_t *tail, uint8_t &taillen) const; + bool HasZeroTail (const uint8_t *tail, uint8_t taillen) const; + + std::list
    m_addressList; + std::list m_prefixList; + PbbAddressTlvBlock m_addressTlvList; + + mutable uint32_t m_refCount; +}; + +/** + * \brief Concrete IPv4 specific PbbAddressBlock. + * + * This address block will only contain IPv4 addresses. + */ +class PbbAddressBlockIpv4 : public PbbAddressBlock +{ +public: + PbbAddressBlockIpv4 (); + virtual ~PbbAddressBlockIpv4 (); + +protected: + virtual uint8_t GetAddressLength (void) const; + + virtual void SerializeAddress (uint8_t *buffer, ConstAddressIterator iter) const; + virtual Address DeserializeAddress (uint8_t *buffer) const; + virtual void PrintAddress (std::ostream &os, ConstAddressIterator iter) const; +}; + +/** + * \brief Concrete IPv6 specific PbbAddressBlock. + * + * This address block will only contain IPv6 addresses. + */ +class PbbAddressBlockIpv6 : public PbbAddressBlock +{ +public: + PbbAddressBlockIpv6 (); + virtual ~PbbAddressBlockIpv6 (); + +protected: + virtual uint8_t GetAddressLength (void) const; + + virtual void SerializeAddress (uint8_t *buffer, ConstAddressIterator iter) const; + virtual Address DeserializeAddress (uint8_t *buffer) const; + virtual void PrintAddress (std::ostream &os, ConstAddressIterator iter) const; +}; + +/** + * \brief A packet or message TLV + */ +class PbbTlv +{ +public: + PbbTlv (void); + + /** + * \brief Sets the type of this TLV. + * \param type the type value to set. + */ + void SetType (uint8_t type); + + /** + * \return the type of this TLV. + */ + uint8_t GetType (void) const; + + /** + * \brief Sets the type extension of this TLV. + * \param type the type extension value to set. + * + * The type extension is like a sub-type used to further distinguish between + * TLVs of the same type. + */ + void SetTypeExt (uint8_t type); + + /** + * \return the type extension for this TLV. + * + * Calling this while HasTypeExt is False is undefined. Make sure you check + * it first. This will be checked by an assert in debug builds. + */ + uint8_t GetTypeExt (void) const; + + /** + * \brief Tests whether or not this TLV has a type extension. + * \return true if this TLV has a type extension, false otherwise. + * + * This should be called before calling GetTypeExt to make sure there + * actually is one. + */ + bool HasTypeExt (void) const; + + /** + * \brief Sets the value of this message to the specified buffer. + * \param start a buffer instance. + * + * The buffer is _not_ copied until this TLV is serialized. You should not + * change the contents of the buffer you pass in to this function. + */ + void SetValue (Buffer start); + + /** + * \brief Sets the value of this message to a buffer with the specified data. + * \param buffer a pointer to data to put in the TLVs buffer. + * \param size the size of the buffer. + * + * The buffer *is copied* into a *new buffer instance*. You can free the + * data in the buffer provided anytime you wish. + */ + void SetValue (const uint8_t * buffer, uint32_t size); + + /** + * \return a Buffer pointing to the value of this TLV. + * + * Calling this while HasValue is False is undefined. Make sure you check it + * first. This will be checked by an assert in debug builds. + */ + Buffer GetValue (void) const; + + /** + * \brief Tests whether or not this TLV has a value. + * \return true if this tlv has a TLV, false otherwise. + * + * This should be called before calling GetTypeExt to make sure there + * actually is one. + */ + bool HasValue (void) const; + + /* Smart pointer methods */ + void Ref (void) const; + void Unref (void) const; + + /** + * \return The size (in bytes) needed to serialize this TLV. + */ + uint32_t GetSerializedSize (void) const; + + /** + * \brief Serializes this TLV into the specified buffer. + * \param start a reference to the point in a buffer to begin serializing. + * + * Users should not need to call this. TLVs will be serialized by their + * containing blocks. + */ + void Serialize (Buffer::Iterator &start) const; + + /** + * \brief Deserializes a TLV from the specified buffer. + * \param start a reference to the point in a buffer to begin deserializing. + * + * Users should not need to call this. TLVs will be deserialized by their + * containing blocks. + */ + void Deserialize (Buffer::Iterator &start); + + /** + * \brief Pretty-prints the contents of this TLV. + * \param os a stream object to print to. + */ + void Print (std::ostream &os) const; + + /** + * \brief Pretty-prints the contents of this TLV, with specified indentation. + * \param os a stream object to print to. + * \param level level of indentation. + * + * This probably never needs to be called by users. This is used when + * recursively printing sub-objects. + */ + void Print (std::ostream &os, int level) const; + + bool operator== (const PbbTlv &other) const; + bool operator!= (const PbbTlv &other) const; + +protected: + void SetIndexStart (uint8_t index); + uint8_t GetIndexStart (void) const; + bool HasIndexStart (void) const; + + void SetIndexStop (uint8_t index); + uint8_t GetIndexStop (void) const; + bool HasIndexStop (void) const; + + void SetMultivalue (bool isMultivalue); + bool IsMultivalue (void) const; + +private: + uint8_t m_type; + + bool m_hasTypeExt; + uint8_t m_typeExt; + + bool m_hasIndexStart; + uint8_t m_indexStart; + + bool m_hasIndexStop; + uint8_t m_indexStop; + + bool m_isMultivalue; + bool m_hasValue; + Buffer m_value; + + mutable uint32_t m_refCount; +}; + +/** + * \brief An Address TLV + */ +class PbbAddressTlv : public PbbTlv +{ +public: + /** + * \brief Sets the index of the first address in the associated address block + * that this address TLV applies to. + * \param index the index of the first address. + */ + void SetIndexStart (uint8_t index); + + /** + * \return the first (inclusive) index of the address in the corresponding + * address block that this TLV applies to. + * + * Calling this while HasIndexStart is False is undefined. Make sure you + * check it first. This will be checked by an assert in debug builds. + */ + uint8_t GetIndexStart (void) const; + + /** + * \brief Tests whether or not this address TLV has a start index. + * \return true if this address TLV has a start index, false otherwise. + * + * This should be called before calling GetIndexStart to make sure there + * actually is one. + */ + bool HasIndexStart (void) const; + + /** + * \brief Sets the index of the last address in the associated address block + * that this address TLV applies to. + * \param index the index of the last address. + */ + void SetIndexStop (uint8_t index); + + /** + * \return the last (inclusive) index of the address in the corresponding + * PbbAddressBlock that this TLV applies to. + * + * Calling this while HasIndexStop is False is undefined. Make sure you + * check it first. This will be checked by an assert in debug builds. + */ + uint8_t GetIndexStop (void) const; + + /** + * \brief Tests whether or not this address TLV has a stop index. + * \return true if this address TLV has a stop index, false otherwise. + * + * This should be called before calling GetIndexStop to make sure there + * actually is one. + */ + bool HasIndexStop (void) const; + + /** + * \brief Sets whether or not this address TLV is "multivalue" + * \param isMultivalue whether or not this address TLV should be multivalue. + * + * If true, this means the value associated with this TLV should be divided + * evenly into (GetIndexStop() - GetIndexStart() + 1) values. Otherwise, the + * value is one single value that applies to each address in the range. + */ + void SetMultivalue (bool isMultivalue); + + /** + * \brief Tests whether or not this address TLV is "multivalue" + * \return whether this address TLV is multivalue or not. + */ + bool IsMultivalue (void) const; +}; + +} /* namespace ns3 */ + +#endif /* PACKETBB_H */ diff --git a/src/node/test-packetbb.cc b/src/node/test-packetbb.cc new file mode 100644 index 000000000..28f9fa1d5 --- /dev/null +++ b/src/node/test-packetbb.cc @@ -0,0 +1,3835 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* vim: set ts=2 sw=2 sta expandtab ai si cin: */ +/* + * Copyright (c) 2009 Drexel University + * + * 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: Tom Wambold + */ + +#include + +#include "ns3/ptr.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv6-address.h" +#include "ns3/packetbb.h" + +using namespace std; +using namespace ns3; + +class PacketBBTester +{ +public: + PacketBBTester (int testnum, PbbPacket &reference, const uint8_t * buffer, + uint32_t size) : + m_refPacket(reference) + { + m_refBuffer.AddAtStart (size); + m_refBuffer.Begin ().Write (buffer, size); + + cout << "Test " << testnum << " - "; + Test (); + } + + void Test (void) + { + if (TestSerialize ()) + { + cout << "Serialize Pass, "; + } + else + { + cout << "Serialize Fail, "; + } + + if (TestDeserialize ()) + { + cout << "Deserialize Pass"; + } + else + { + cout << "Deserialize Fail"; + } + + cout << endl; + } + + bool TestSerialize (void) + { + Buffer newBuffer; + newBuffer.AddAtStart (m_refPacket.GetSerializedSize ()); + m_refPacket.Serialize (newBuffer.Begin ()); + return CompareBuffers (m_refBuffer, newBuffer); + } + + bool TestDeserialize (void) + { + PbbPacket newPacket; + if (newPacket.Deserialize (m_refBuffer.Begin ()) != m_refBuffer.GetSize ()) + { + return false; + } + return m_refPacket == newPacket; + } + +private: + static bool CompareBuffers (Buffer a, Buffer b) + { + const uint8_t * abuf = a.PeekData (); + const uint8_t * bbuf = b.PeekData (); + + for (unsigned int i = 0; i < a.GetSize (); i++) + { + if (abuf[i] != bbuf[i]) + { + cout << "Difference - [" << i << "] - " << (int)abuf[i] << " - " << (int)bbuf[i] << endl; + } + } + + if (a.GetSize () != b.GetSize ()) + { + cout << "Buffers differ in size: " << a.GetSize () << ", " << b.GetSize() << endl; + return false; + } + + if (memcmp (a.PeekData (), b.PeekData (), a.GetSize ()) != 0) + { + return false; + } + + return true; + } + + Buffer m_refBuffer; + PbbPacket &m_refPacket; +}; + +int main (void) +{ + /* These tests are from: + * http://interop08.thomasclausen.org/packets-and-dumps.txt + */ + int testnum = 1; + + /* Test 1 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * `------------------ + */ + { + PbbPacket packet; + uint8_t buffer[] = {0x00}; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 2 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 8 + * | * Packet seq number: 2 + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (2); + uint8_t buffer[] = {0x08, 0x00, 0x02}; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 3 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 3 + * `------------------ + * This test has the phastlv flag set to 1 with no tlvs. + * I'll come back to this one later. + { + PbbPacket packet; + packet.SetSequenceNumber (3); + uint8_t buffer[] = {0x0c, 0x00, 0x03, 0x00, 0x00}; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + */ + std::cout << "Skipping test " << testnum++ << std::endl; + + /* Test 4 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 4 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (4); + + Ptr tlv = Create(); + tlv->SetType (1); + + packet.TlvPushBack (tlv); + uint8_t buffer[] = { + 0x0c, 0x00, 0x04, 0x00, + 0x02, 0x01, 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 5 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 5 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | | - TLV + * | | Flags = 128 + * | | Type = 2; Type ext. = 100; Value = (warning: parameter is NULL) + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (5); + + Ptr tlv1 = Create(); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr tlv2 = Create(); + tlv2->SetType (2); + tlv2->SetTypeExt (100); + packet.TlvPushBack (tlv2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x05, 0x00, + 0x05, 0x01, 0x00, 0x02, + 0x80, 0x64 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 6 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 6 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | | - TLV + * | | Flags = 144 + * | | Type = 2; Type ext. = 100; Value = 01 02 03 04 + * | | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (6); + + Ptr tlv1 = Create(); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr tlv2 = Create(); + tlv2->SetType (2); + tlv2->SetTypeExt (100); + + uint8_t tlv2val[] = {1, 2, 3, 4}; + tlv2->SetValue(tlv2val, sizeof(tlv2val)); + + packet.TlvPushBack (tlv2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x06, 0x00, + 0x0a, 0x01, 0x00, 0x02, + 0x90, 0x64, 0x04, 0x01, + 0x02, 0x03, 0x04 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 7 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 7 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | | - TLV + * | | Flags = 152 + * | | Type = 2; Type ext. = 100; Value = 00 01 02 03 + * | | 04 05 06 07 + * | | 08 09 0a 0b + * | | 0c 0d 0e 0f + * | | 10 11 12 13 + * | | 14 15 16 17 + * | | 18 19 1a 1b + * | | 1c 1d 1e 1f + * | | 20 21 22 23 + * | | 24 25 26 27 + * | | 28 29 2a 2b + * | | 2c 2d 2e 2f + * | | 30 31 32 33 + * | | 34 35 36 37 + * | | 38 39 3a 3b + * | | 3c 3d 3e 3f + * | | 40 41 42 43 + * | | 44 45 46 47 + * | | 48 49 4a 4b + * | | 4c 4d 4e 4f + * | | 50 51 52 53 + * | | 54 55 56 57 + * | | 58 59 5a 5b + * | | 5c 5d 5e 5f + * | | 60 61 62 63 + * | | 64 65 66 67 + * | | 68 69 6a 6b + * | | 6c 6d 6e 6f + * | | 70 71 72 73 + * | | 74 75 76 77 + * | | 78 79 7a 7b + * | | 7c 7d 7e 7f + * | | 80 81 82 83 + * | | 84 85 86 87 + * | | 88 89 8a 8b + * | | 8c 8d 8e 8f + * | | 90 91 92 93 + * | | 94 95 96 97 + * | | 98 99 9a 9b + * | | 9c 9d 9e 9f + * | | a0 a1 a2 a3 + * | | a4 a5 a6 a7 + * | | a8 a9 aa ab + * | | ac ad ae af + * | | b0 b1 b2 b3 + * | | b4 b5 b6 b7 + * | | b8 b9 ba bb + * | | bc bd be bf + * | | c0 c1 c2 c3 + * | | c4 c5 c6 c7 + * | | c8 c9 ca cb + * | | cc cd ce cf + * | | d0 d1 d2 d3 + * | | d4 d5 d6 d7 + * | | d8 d9 da db + * | | dc dd de df + * | | e0 e1 e2 e3 + * | | e4 e5 e6 e7 + * | | e8 e9 ea eb + * | | ec ed ee ef + * | | f0 f1 f2 f3 + * | | f4 f5 f6 f7 + * | | f8 f9 fa fb + * | | fc fd fe 00 + * | | 01 02 03 04 + * | | 05 06 07 08 + * | | 09 0a 0b 0c + * | | 0d 0e 0f 10 + * | | 11 12 13 14 + * | | 15 16 17 18 + * | | 19 1a 1b 1c + * | | 1d 1e 1f 20 + * | | 21 22 23 24 + * | | 25 26 27 28 + * | | 29 2a 2b 2c + * | | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (7); + + Ptr tlv1 = Create(); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr tlv2 = Create(); + tlv2->SetType (2); + tlv2->SetTypeExt (100); + + uint8_t tlv2val[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, + 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c + }; + tlv2->SetValue(tlv2val, sizeof(tlv2val)); + + packet.TlvPushBack (tlv2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x07, 0x01, + 0x33, 0x01, 0x00, 0x02, + 0x98, 0x64, 0x01, 0x2c, + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, + 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 8 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 8 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (8); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 9 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 9 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 128 + * | | * Originator address: 10.0.0.1 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (9); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x09, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00, 0x02, 0x83, 0x00, /* [14] used to be 0x80 */ + 0x0a, 0x0a, 0x00, 0x00, + 0x01, 0x00, 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 10 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 10 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 160 + * | | * Originator address: 10.0.0.1 + * | | * Hop count: 1 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (10); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + msg2->SetHopCount (1); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0a, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00, 0x02, 0xa3, 0x00, /* [14] used to be 0xa0 */ + 0x0b, 0x0a, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 11 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 11 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 224 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (11); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0b, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00, 0x02, 0xe3, 0x00, /* [14] used to be 0xe0 */ + 0x0c, 0x0a, 0x00, 0x00, + 0x01, 0xff, 0x01, 0x00, + 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 12 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 12 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (12); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0c, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */ + 0x0e, 0x0a, 0x00, 0x00, + 0x01, 0xff, 0x01, 0x30, + 0x39, 0x00, 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 13 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 13 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (13); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0d, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x06, 0x00, + 0x00, 0x02, 0xf3, 0x00, /* [14] - 0xf0 */ + 0x0e, 0x0a, 0x00, 0x00, + 0x01, 0xff, 0x01, 0x30, + 0x39, 0x00, 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 14 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 14 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (14); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress(Ipv4Address("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0e, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x0e, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 15 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 15 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (1 addresses) + * | | - 0.0.0.0/32 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (15); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("0.0.0.0")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x0f, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 16 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 16 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (1 addresses) + * | | - 255.255.255.255/32 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (16); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("255.255.255.255")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x10, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 17 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 17 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (1 addresses) + * | | - 0.0.0.1/32 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (17); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("0.0.0.1")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x11, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 18 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 18 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (1 addresses) + * | | - 10.0.0.0/32 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (18); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x12, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x01, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 19 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 19 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (1 addresses) + * | | - 10.0.0.1/32 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (19); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.1")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x13, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x16, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x01, 0x00, 0x0a, + 0x00, 0x00, 0x01, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 20 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 20 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.1/32 + * | | - 10.0.0.2/32 + * | | - Flags = 128 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (20); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.1")); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x14, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x18, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0x80, 0x03, + 0x0a, 0x00, 0x00, 0x01, + 0x02, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 21 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 21 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (21); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x15, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x1a, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 22 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 22 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (2 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - Flags = 32 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (22); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x16, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x21, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x02, 0x20, 0x03, + 0x0a, 0x0b, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 23 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 23 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (23); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x17, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x32, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 24 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 24 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (24); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + Ptr msg2a2tlv1 = Create (); + msg2a2tlv1->SetType (1); + msg2a2->TlvPushBack (msg2a2tlv1); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x18, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x34, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x00, + 0x02, 0x01, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 25 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 25 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 64 + * | | Index-start = 1 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (25); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + Ptr msg2a2tlv1 = Create (); + msg2a2tlv1->SetType (1); + msg2a2tlv1->SetIndexStart (1); + msg2a2->TlvPushBack (msg2a2tlv1); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x19, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x35, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x00, + 0x03, 0x01, 0x40, 0x01, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 26 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 26 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 32 + * | | Index-start = 1 + * | | Index-stop = 3 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (26); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + Ptr msg2a2tlv1 = Create (); + msg2a2tlv1->SetType (1); + msg2a2tlv1->SetIndexStart (1); + msg2a2tlv1->SetIndexStop (3); + msg2a2->TlvPushBack (msg2a2tlv1); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x1a, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x36, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x00, + 0x04, 0x01, 0x20, 0x01, + 0x03, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 27 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 27 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 52 + * | | Index-start = 1 + * | | Index-stop = 3 + * | | Type = 1; Value = 01 02 03 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (27); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + Ptr msg2a2tlv1 = Create (); + msg2a2tlv1->SetType (1); + msg2a2tlv1->SetIndexStart (1); + msg2a2tlv1->SetIndexStop (3); + + uint8_t value[] = {1, 2, 3}; + msg2a2tlv1->SetValue(value, sizeof (value)); + msg2a2tlv1->SetMultivalue (true); + + msg2a2->TlvPushBack (msg2a2tlv1); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x1b, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x00, 0x3a, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x00, + 0x08, 0x01, 0x34, 0x01, + 0x03, 0x03, 0x01, 0x02, + 0x03, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 28 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 28 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 56 + * | | Index-start = 1 + * | | Index-stop = 3 + * | | Type = 1; Value = 00 01 02 03 + * | | 04 05 06 07 + * | | 08 09 0a 0b + * | | 0c 0d 0e 0f + * | | 10 11 12 13 + * | | 14 15 16 17 + * | | 18 19 1a 1b + * | | 1c 1d 1e 1f + * | | 20 21 22 23 + * | | 24 25 26 27 + * | | 28 29 2a 2b + * | | 2c 2d 2e 2f + * | | 30 31 32 33 + * | | 34 35 36 37 + * | | 38 39 3a 3b + * | | 3c 3d 3e 3f + * | | 40 41 42 43 + * | | 44 45 46 47 + * | | 48 49 4a 4b + * | | 4c 4d 4e 4f + * | | 50 51 52 53 + * | | 54 55 56 57 + * | | 58 59 5a 5b + * | | 5c 5d 5e 5f + * | | 60 61 62 63 + * | | 64 65 66 67 + * | | 68 69 6a 6b + * | | 6c 6d 6e 6f + * | | 70 71 72 73 + * | | 74 75 76 77 + * | | 78 79 7a 7b + * | | 7c 7d 7e 7f + * | | 80 81 82 83 + * | | 84 85 86 87 + * | | 88 89 8a 8b + * | | 8c 8d 8e 8f + * | | 90 91 92 93 + * | | 94 95 96 97 + * | | 98 99 9a 9b + * | | 9c 9d 9e 9f + * | | a0 a1 a2 a3 + * | | a4 a5 a6 a7 + * | | a8 a9 aa ab + * | | ac ad ae af + * | | b0 b1 b2 b3 + * | | b4 b5 b6 b7 + * | | b8 b9 ba bb + * | | bc bd be bf + * | | c0 c1 c2 c3 + * | | c4 c5 c6 c7 + * | | c8 c9 ca cb + * | | cc cd ce cf + * | | d0 d1 d2 d3 + * | | d4 d5 d6 d7 + * | | d8 d9 da db + * | | dc dd de df + * | | e0 e1 e2 e3 + * | | e4 e5 e6 e7 + * | | e8 e9 ea eb + * | | ec ed ee ef + * | | f0 f1 f2 f3 + * | | f4 f5 f6 f7 + * | | f8 f9 fa fb + * | | fc fd fe 00 + * | | 01 02 03 04 + * | | 05 06 07 08 + * | | 09 0a 0b 0c + * | | 0d 0e 0f 10 + * | | 11 12 13 14 + * | | 15 16 17 18 + * | | 19 1a 1b 1c + * | | 1d 1e 1f 20 + * | | 21 22 23 24 + * | | 25 26 27 28 + * | | 29 2a 2b 2c + * | | + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (28); + + Ptr tlv1 = Create (); + tlv1->SetType (1); + packet.TlvPushBack (tlv1); + + Ptr msg1 = Create (); + msg1->SetType (1); + + Ptr msg1tlv1 = Create (); + msg1tlv1->SetType (1); + msg1->TlvPushBack (msg1tlv1); + + packet.MessagePushBack (msg1); + + Ptr msg2 = Create (); + msg2->SetType (2); + msg2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + msg2->SetHopLimit (255); + msg2->SetHopCount (1); + msg2->SetSequenceNumber (12345); + + Ptr msg2a1 = Create (); + msg2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + msg2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + msg2->AddressBlockPushBack (msg2a1); + + Ptr msg2a2 = Create (); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + msg2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (32); + msg2a2->PrefixPushBack (16); + msg2a2->PrefixPushBack (24); + + Ptr msg2a2tlv1 = Create (); + msg2a2tlv1->SetType (1); + msg2a2tlv1->SetIndexStart (1); + msg2a2tlv1->SetIndexStop (3); + + uint8_t value[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, + 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + }; + msg2a2tlv1->SetValue(value, sizeof (value)); + + msg2a2->TlvPushBack (msg2a2tlv1); + + msg2->AddressBlockPushBack (msg2a2); + + packet.MessagePushBack (msg2); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x1c, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x03, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x01, 0x64, 0x0a, /* [16] - 0xf0 */ + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x01, + 0x32, 0x01, 0x38, 0x01, + 0x03, 0x01, 0x2c, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, + 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, + 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, + 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, + 0x9d, 0x9e, 0x9f, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 29 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 1 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x0f, 0x00, + 0x06, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 30 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x16, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00 + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 31 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (1 addresses) + * | | - 10::1/128 + * | | - Flags = 0 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + Ptr m1a1 = Create (); + m1a1->AddressPushBack (Ipv6Address ("10::1")); + m1->AddressBlockPushBack (m1a1); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x2a, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 32 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::1/128 + * | | - 10::2/128 + * | | - Flags = 128 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + Ptr m1a1 = Create (); + m1a1->AddressPushBack (Ipv6Address ("10::1")); + m1a1->AddressPushBack (Ipv6Address ("10::2")); + m1->AddressBlockPushBack (m1a1); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x2c, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, + 0x80, 0x0f, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x00, + 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 33 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::2/128 + * | | - 10::11:2/128 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + Ptr m1a1 = Create (); + m1a1->AddressPushBack (Ipv6Address ("10::2")); + m1a1->AddressPushBack (Ipv6Address ("10::11:2")); + m1->AddressBlockPushBack (m1a1); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x2d, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, + 0xc0, 0x0d, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x11, + 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 34 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::2/128 + * | | - 10::11:2/128 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (2 addresses) + * | | - 10::/128 + * | | - 11::/128 + * | | - Flags = 160 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + Ptr m1a1 = Create (); + m1a1->AddressPushBack (Ipv6Address ("10::2")); + m1a1->AddressPushBack (Ipv6Address ("10::11:2")); + m1->AddressBlockPushBack (m1a1); + + Ptr m1a2 = Create (); + m1a2->AddressPushBack (Ipv6Address ("10::")); + m1a2->AddressPushBack (Ipv6Address ("11::")); + m1->AddressBlockPushBack (m1a2); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x36, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, + 0xc0, 0x0d, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x11, + 0x00, 0x00, 0x02, 0xa0, + 0x01, 0x00, 0x0e, 0x10, + 0x11, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 35 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 0 + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::2/128 + * | | - 10::11:2/128 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10::/128 + * | | - 11::/128 + * | | - 10::5/64 + * | | - 10::6/48 + * | | - Flags = 136 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + + Ptr m1 = Create (); + m1->SetType(1); + m1->SetOriginatorAddress (Ipv6Address("abcd::1")); + + Ptr m1a1 = Create (); + m1a1->AddressPushBack (Ipv6Address ("10::2")); + m1a1->AddressPushBack (Ipv6Address ("10::11:2")); + m1->AddressBlockPushBack (m1a1); + + Ptr m1a2 = Create (); + m1a2->AddressPushBack (Ipv6Address ("10::")); + m1a2->AddressPushBack (Ipv6Address ("11::")); + m1a2->AddressPushBack (Ipv6Address ("10::5")); + m1a2->AddressPushBack (Ipv6Address ("10::6")); + m1a2->PrefixPushBack (128); + m1a2->PrefixPushBack (128); + m1a2->PrefixPushBack (64); + m1a2->PrefixPushBack (48); + m1->AddressBlockPushBack (m1a2); + + packet.MessagePushBack (m1); + + uint8_t buffer[] = { + 0x00, 0x01, 0x8f, 0x00, + 0x73, 0xab, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, + 0xc0, 0x0d, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x11, + 0x00, 0x00, 0x04, 0x88, + 0x01, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x80, 0x80, + 0x40, 0x30, 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 36 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 29 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 56 + * | | Index-start = 1 + * | | Index-stop = 3 + * | | Type = 1; Value = 00 01 02 03 + * | | 04 05 06 07 + * | | 08 09 0a 0b + * | | 0c 0d 0e 0f + * | | 10 11 12 13 + * | | 14 15 16 17 + * | | 18 19 1a 1b + * | | 1c 1d 1e 1f + * | | 20 21 22 23 + * | | 24 25 26 27 + * | | 28 29 2a 2b + * | | 2c 2d 2e 2f + * | | 30 31 32 33 + * | | 34 35 36 37 + * | | 38 39 3a 3b + * | | 3c 3d 3e 3f + * | | 40 41 42 43 + * | | 44 45 46 47 + * | | 48 49 4a 4b + * | | 4c 4d 4e 4f + * | | 50 51 52 53 + * | | 54 55 56 57 + * | | 58 59 5a 5b + * | | 5c 5d 5e 5f + * | | 60 61 62 63 + * | | 64 65 66 67 + * | | 68 69 6a 6b + * | | 6c 6d 6e 6f + * | | 70 71 72 73 + * | | 74 75 76 77 + * | | 78 79 7a 7b + * | | 7c 7d 7e 7f + * | | 80 81 82 83 + * | | 84 85 86 87 + * | | 88 89 8a 8b + * | | 8c 8d 8e 8f + * | | 90 91 92 93 + * | | 94 95 96 97 + * | | 98 99 9a 9b + * | | 9c 9d 9e 9f + * | | a0 a1 a2 a3 + * | | a4 a5 a6 a7 + * | | a8 a9 aa ab + * | | ac ad ae af + * | | b0 b1 b2 b3 + * | | b4 b5 b6 b7 + * | | b8 b9 ba bb + * | | bc bd be bf + * | | c0 c1 c2 c3 + * | | c4 c5 c6 c7 + * | | c8 c9 ca cb + * | | cc cd ce cf + * | | d0 d1 d2 d3 + * | | d4 d5 d6 d7 + * | | d8 d9 da db + * | | dc dd de df + * | | e0 e1 e2 e3 + * | | e4 e5 e6 e7 + * | | e8 e9 ea eb + * | | ec ed ee ef + * | | f0 f1 f2 f3 + * | | f4 f5 f6 f7 + * | | f8 f9 fa fb + * | | fc fd fe 00 + * | | 01 02 03 04 + * | | 05 06 07 08 + * | | 09 0a 0b 0c + * | | 0d 0e 0f 10 + * | | 11 12 13 14 + * | | 15 16 17 18 + * | | 19 1a 1b 1c + * | | 1d 1e 1f 20 + * | | 21 22 23 24 + * | | 25 26 27 28 + * | | 29 2a 2b 2c + * | | + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::2/128 + * | | - 10::11:2/128 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10::/128 + * | | - 11::/128 + * | | - 10::5/64 + * | | - 10::6/48 + * | | - Flags = 136 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (29); + + Ptr ptlv1 = Create (); + ptlv1->SetType (1); + packet.TlvPushBack (ptlv1); + + Ptr m1 = Create (); + m1->SetType (1); + + Ptr m1tlv1 = Create (); + m1tlv1->SetType (1); + m1->TlvPushBack (m1tlv1); + packet.MessagePushBack (m1); + + Ptr m2 = Create (); + m2->SetType (2); + m2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + m2->SetHopLimit (255); + m2->SetHopCount (1); + m2->SetSequenceNumber (12345); + + Ptr m2a1 = Create (); + m2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + m2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + m2->AddressBlockPushBack (m2a1); + + Ptr m2a2 = Create (); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + m2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + m2a2->PrefixPushBack (32); + m2a2->PrefixPushBack (32); + m2a2->PrefixPushBack (16); + m2a2->PrefixPushBack (24); + + Ptr m2a2tlv1 = Create (); + m2a2tlv1->SetType (1); + m2a2tlv1->SetIndexStart (1); + m2a2tlv1->SetIndexStop (3); + + uint8_t value[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, + 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + }; + m2a2tlv1->SetValue (value, sizeof(value)); + m2a2->TlvPushBack (m2a2tlv1); + + m2->AddressBlockPushBack (m2a2); + packet.MessagePushBack (m2); + + Ptr m3 = Create (); + m3->SetType (1); + m3->SetOriginatorAddress (Ipv6Address ("abcd::1")); + + Ptr m3a1 = Create (); + m3a1->AddressPushBack (Ipv6Address ("10::2")); + m3a1->AddressPushBack (Ipv6Address ("10::11:2")); + m3->AddressBlockPushBack (m3a1); + + Ptr m3a2 = Create (); + m3a2->AddressPushBack (Ipv6Address ("10::")); + m3a2->AddressPushBack (Ipv6Address ("11::")); + m3a2->AddressPushBack (Ipv6Address ("10::5")); + m3a2->AddressPushBack (Ipv6Address ("10::6")); + m3a2->PrefixPushBack (128); + m3a2->PrefixPushBack (128); + m3a2->PrefixPushBack (64); + m3a2->PrefixPushBack (48); + + m3->AddressBlockPushBack (m3a2); + packet.MessagePushBack (m3); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x1d, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x0f, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x01, 0x64, 0x0a, + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x01, + 0x32, 0x01, 0x38, 0x01, + 0x03, 0x01, 0x2c, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, + 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, + 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, + 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, + 0x9d, 0x9e, 0x9f, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x01, + 0x8f, 0x00, 0x73, 0xab, + 0xcd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0xc0, 0x0d, + 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x02, + 0x00, 0x11, 0x00, 0x00, + 0x04, 0x88, 0x01, 0x00, + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x05, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, + 0x80, 0x80, 0x40, 0x30, + 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } + + /* Test 37 + * ,------------------ + * | PACKET + * |------------------ + * | * Packet version: 0 + * | * Packet flags: 12 + * | * Packet seq number: 30 + * | | * Packet TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 0 + * | | * Message TLV Block + * | | - TLV + * | | Flags = 0 + * | | Type = 1; Value = (warning: parameter is NULL) + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 2 + * | | * Message flags: 240 + * | | * Originator address: 10.0.0.1 + * | | * Hop limit: 255 + * | | * Hop count: 1 + * | | * Message seq number: 12345 + * | | - Address block (2 addresses) + * | | - 10.0.0.2/32 + * | | - 10.1.1.2/32 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10.0.0.0/32 + * | | - 11.0.0.0/32 + * | | - 10.0.0.5/16 + * | | - 10.0.0.6/24 + * | | - Flags = 8 + * | | - ADDRESS TLV block (1 TLVs) + * | | - TLV + * | | Flags = 56 + * | | Index-start = 1 + * | | Index-stop = 3 + * | | Type = 1; Value = 00 01 02 03 + * | | 04 05 06 07 + * | | 08 09 0a 0b + * | | 0c 0d 0e 0f + * | | 10 11 12 13 + * | | 14 15 16 17 + * | | 18 19 1a 1b + * | | 1c 1d 1e 1f + * | | 20 21 22 23 + * | | 24 25 26 27 + * | | 28 29 2a 2b + * | | 2c 2d 2e 2f + * | | 30 31 32 33 + * | | 34 35 36 37 + * | | 38 39 3a 3b + * | | 3c 3d 3e 3f + * | | 40 41 42 43 + * | | 44 45 46 47 + * | | 48 49 4a 4b + * | | 4c 4d 4e 4f + * | | 50 51 52 53 + * | | 54 55 56 57 + * | | 58 59 5a 5b + * | | 5c 5d 5e 5f + * | | 60 61 62 63 + * | | 64 65 66 67 + * | | 68 69 6a 6b + * | | 6c 6d 6e 6f + * | | 70 71 72 73 + * | | 74 75 76 77 + * | | 78 79 7a 7b + * | | 7c 7d 7e 7f + * | | 80 81 82 83 + * | | 84 85 86 87 + * | | 88 89 8a 8b + * | | 8c 8d 8e 8f + * | | 90 91 92 93 + * | | 94 95 96 97 + * | | 98 99 9a 9b + * | | 9c 9d 9e 9f + * | | a0 a1 a2 a3 + * | | a4 a5 a6 a7 + * | | a8 a9 aa ab + * | | ac ad ae af + * | | b0 b1 b2 b3 + * | | b4 b5 b6 b7 + * | | b8 b9 ba bb + * | | bc bd be bf + * | | c0 c1 c2 c3 + * | | c4 c5 c6 c7 + * | | c8 c9 ca cb + * | | cc cd ce cf + * | | d0 d1 d2 d3 + * | | d4 d5 d6 d7 + * | | d8 d9 da db + * | | dc dd de df + * | | e0 e1 e2 e3 + * | | e4 e5 e6 e7 + * | | e8 e9 ea eb + * | | ec ed ee ef + * | | f0 f1 f2 f3 + * | | f4 f5 f6 f7 + * | | f8 f9 fa fb + * | | fc fd fe 00 + * | | 01 02 03 04 + * | | 05 06 07 08 + * | | 09 0a 0b 0c + * | | 0d 0e 0f 10 + * | | 11 12 13 14 + * | | 15 16 17 18 + * | | 19 1a 1b 1c + * | | 1d 1e 1f 20 + * | | 21 22 23 24 + * | | 25 26 27 28 + * | | 29 2a 2b 2c + * | | + * | `------------------- + * | + * | ,------------------- + * | | MESSAGE + * | |------------------- + * | | * Message type: 1 + * | | * Message flags: 129 + * | | * Originator address: abcd::1 + * | | - Address block (2 addresses) + * | | - 10::2/128 + * | | - 10::11:2/128 + * | | - Flags = 192 + * | | - ADDRESS TLV block (0 TLVs) + * | | - Address block (4 addresses) + * | | - 10::/128 + * | | - 11::/128 + * | | - 10::5/64 + * | | - 10::6/48 + * | | - Flags = 136 + * | | - ADDRESS TLV block (0 TLVs) + * | `------------------- + * | + * `------------------ + */ + { + PbbPacket packet; + packet.SetSequenceNumber (30); + + Ptr ptlv1 = Create (); + ptlv1->SetType (1); + packet.TlvPushBack (ptlv1); + + Ptr m1 = Create (); + m1->SetType (1); + + Ptr m1tlv1 = Create (); + m1tlv1->SetType (1); + m1->TlvPushBack (m1tlv1); + packet.MessagePushBack (m1); + + Ptr m2 = Create (); + m2->SetType (2); + m2->SetOriginatorAddress (Ipv4Address ("10.0.0.1")); + m2->SetHopLimit (255); + m2->SetHopCount (1); + m2->SetSequenceNumber (12345); + + Ptr m2a1 = Create (); + m2a1->AddressPushBack (Ipv4Address ("10.0.0.2")); + m2a1->AddressPushBack (Ipv4Address ("10.1.1.2")); + m2->AddressBlockPushBack (m2a1); + + Ptr m2a2 = Create (); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.0")); + m2a2->AddressPushBack (Ipv4Address ("11.0.0.0")); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.5")); + m2a2->AddressPushBack (Ipv4Address ("10.0.0.6")); + m2a2->PrefixPushBack (32); + m2a2->PrefixPushBack (32); + m2a2->PrefixPushBack (16); + m2a2->PrefixPushBack (24); + + Ptr m2a2tlv1 = Create (); + m2a2tlv1->SetType (1); + m2a2tlv1->SetIndexStart (1); + m2a2tlv1->SetIndexStop (3); + + uint8_t value[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, + 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, + 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, + 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + }; + m2a2tlv1->SetValue (value, sizeof(value)); + m2a2->TlvPushBack (m2a2tlv1); + + m2->AddressBlockPushBack (m2a2); + packet.MessagePushBack (m2); + + Ptr m3 = Create (); + m3->SetType (1); + m3->SetOriginatorAddress (Ipv6Address ("abcd::1")); + + Ptr m3a1 = Create (); + m3a1->AddressPushBack (Ipv6Address ("10::2")); + m3a1->AddressPushBack (Ipv6Address ("10::11:2")); + m3->AddressBlockPushBack (m3a1); + + Ptr m3a2 = Create (); + m3a2->AddressPushBack (Ipv6Address ("10::")); + m3a2->AddressPushBack (Ipv6Address ("11::")); + m3a2->AddressPushBack (Ipv6Address ("10::5")); + m3a2->AddressPushBack (Ipv6Address ("10::6")); + m3a2->PrefixPushBack (128); + m3a2->PrefixPushBack (128); + m3a2->PrefixPushBack (64); + m3a2->PrefixPushBack (48); + + m3->AddressBlockPushBack (m3a2); + packet.MessagePushBack (m3); + + uint8_t buffer[] = { + 0x0c, 0x00, 0x1e, 0x00, + 0x02, 0x01, 0x00, 0x01, + 0x0f, 0x00, 0x08, 0x00, + 0x02, 0x01, 0x00, 0x02, + 0xf3, 0x01, 0x64, 0x0a, + 0x00, 0x00, 0x01, 0xff, + 0x01, 0x30, 0x39, 0x00, + 0x00, 0x02, 0xc0, 0x01, + 0x0a, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x00, + 0x00, 0x04, 0x08, 0x0a, + 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x05, 0x0a, + 0x00, 0x00, 0x06, 0x20, + 0x20, 0x10, 0x18, 0x01, + 0x32, 0x01, 0x38, 0x01, + 0x03, 0x01, 0x2c, 0x00, + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, + 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, + 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, + 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, + 0x9d, 0x9e, 0x9f, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, + 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x01, + 0x8f, 0x00, 0x73, 0xab, + 0xcd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0xc0, 0x0d, + 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x02, + 0x00, 0x11, 0x00, 0x00, + 0x04, 0x88, 0x01, 0x00, + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x05, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, + 0x80, 0x80, 0x40, 0x30, + 0x00, 0x00, + }; + PacketBBTester test(testnum++, packet, buffer, sizeof(buffer)); + } +} diff --git a/src/node/wscript b/src/node/wscript index 1430e63ef..416db3879 100644 --- a/src/node/wscript +++ b/src/node/wscript @@ -45,6 +45,7 @@ def build(bld): 'ipv6.cc', 'ipv6-raw-socket-factory.cc', 'ipv6-routing-protocol.cc', + 'packetbb.cc', ] headers = bld.new_task_gen('ns3header') @@ -91,4 +92,5 @@ def build(bld): 'ipv6.h', 'ipv6-raw-socket-factory.h', 'ipv6-routing-protocol.h', + 'packetbb.h', ] diff --git a/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc b/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc new file mode 100644 index 000000000..f18576caa --- /dev/null +++ b/src/test/ns3tcp/ns3tcp-cwnd-test-suite.cc @@ -0,0 +1,381 @@ +/* -*- 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 "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/test.h" +#include "ns3/pcap-file.h" +#include "ns3/config.h" +#include "ns3/string.h" +#include "ns3/uinteger.h" +#include "ns3/data-rate.h" +#include "ns3/inet-socket-address.h" +#include "ns3/point-to-point-helper.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/packet-sink-helper.h" +#include "ns3/tcp-socket-factory.h" +#include "ns3/simulator.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("Ns3CwndTest"); + +// =========================================================================== +// This is a simple test to demonstrate how a known good model (a reference +// implementation) may be used to test another model without resorting to +// storing stimulus or response vectors. +// +// Node zero contains the model under test, in this case the ns-3 TCP +// implementation. Node one contains the reference implementation that we +// assume will generate good test vectors for us. In this case, a Linux +// TCP implementation is used to stimulate the ns-3 TCP model with what we +// assume are perfectly good packets. We watch the ns-3 implementation to +// see what it does in the presence of these assumed good stimuli. +// +// The test is arranged as a typical ns-3 script, but we use the trace system +// to peek into the running system and monitor the ns-3 TCP. +// +// The topology is just two nodes communicating over a point-to-point network. +// The point-to-point network is chosen because it is simple and allows us to +// easily generate pcap traces we can use to separately verify that the ns-3 +// implementation is responding correctly. Once the oopration is verified, we +// enter a list of responses that capture the response succinctly. +// +// node 0 node 1 +// +----------------+ +----------------+ +// | ns-3 TCP | | Linux TCP | +// +----------------+ +----------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +----------------+ +----------------+ +// | point-to-point | | point-to-point | +// +----------------+ +----------------+ +// | | +// +---------------------+ +// 5 Mbps, 2 ms +// +// =========================================================================== +// +class SimpleSource : public Application +{ +public: + + SimpleSource (); + virtual ~SimpleSource(); + + void Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate); + +private: + virtual void StartApplication (void); + virtual void StopApplication (void); + + void ScheduleTx (void); + void SendPacket (void); + + Ptr m_socket; + Address m_peer; + uint32_t m_packetSize; + uint32_t m_nPackets; + DataRate m_dataRate; + EventId m_sendEvent; + bool m_running; + uint32_t m_packetsSent; +}; + +SimpleSource::SimpleSource () + : m_socket (0), + m_peer (), + m_packetSize (0), + m_nPackets (0), + m_dataRate (0), + m_sendEvent (), + m_running (false), + m_packetsSent (0) +{ +} + +SimpleSource::~SimpleSource() +{ + m_socket = 0; +} + +void +SimpleSource::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_nPackets = nPackets; + m_dataRate = dataRate; +} + +void +SimpleSource::StartApplication (void) +{ + m_running = true; + m_packetsSent = 0; + m_socket->Bind (); + m_socket->Connect (m_peer); + SendPacket (); +} + +void +SimpleSource::StopApplication (void) +{ + m_running = false; + + if (m_sendEvent.IsRunning ()) + { + Simulator::Cancel (m_sendEvent); + } + + if (m_socket) + { + m_socket->Close (); + } +} + +void +SimpleSource::SendPacket (void) +{ + Ptr packet = Create (m_packetSize); + m_socket->Send (packet); + + if (++m_packetsSent < m_nPackets) + { + ScheduleTx (); + } +} + +void +SimpleSource::ScheduleTx (void) +{ + if (m_running) + { + Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); + m_sendEvent = Simulator::Schedule (tNext, &SimpleSource::SendPacket, this); + } +} + +class Ns3TcpCwndTestCase : public TestCase +{ +public: + Ns3TcpCwndTestCase (); + virtual ~Ns3TcpCwndTestCase (); + +private: + virtual bool DoRun (void); + bool m_writeResults; + + class CwndEvent { + public: + uint32_t m_oldCwnd; + uint32_t m_newCwnd; + }; + + TestVectors m_responses; + + void CwndChange (uint32_t oldCwnd, uint32_t newCwnd); +}; + +Ns3TcpCwndTestCase::Ns3TcpCwndTestCase () + : TestCase ("Check to see that the ns-3 TCP congestion window works as expected against liblinux2.6.26.so"), + m_writeResults (false) +{ +} + +Ns3TcpCwndTestCase::~Ns3TcpCwndTestCase () +{ +} + +void +Ns3TcpCwndTestCase::CwndChange (uint32_t oldCwnd, uint32_t newCwnd) +{ + CwndEvent event; + + event.m_oldCwnd = oldCwnd; + event.m_newCwnd = newCwnd; + + m_responses.Add (event); +} + +bool +Ns3TcpCwndTestCase::DoRun (void) +{ + // + // Just create two nodes. One (node zero) will be the node with the TCP + // under test which is the ns-3 TCP implementation. The other node (node + // one) will be the node with the reference implementation we use to drive + // the tests. + // + NodeContainer nodes; + nodes.Create (2); + + // + // For this test we'll use a point-to-point net device. It's not as simple + // as a simple-net-device, but it provides nice places to hook trace events + // so we can see what's moving between our nodes. + // + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + + // + // Install the point-to-point devices on both nodes and connec them up. + // + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); + + // + // Install two variants of the internet stack. The first, on node zero + // uses the TCP under test, which is the default ns-3 TCP implementation. + // + InternetStackHelper stack; + stack.Install (nodes.Get (0)); + + // + // The other node, node one, is going to be set up to use a Linux TCP + // implementation that we consider a known good TCP. + // + std::string nscStack = "liblinux2.6.26.so"; + stack.SetTcp ("ns3::NscTcpL4Protocol", "Library", StringValue("liblinux2.6.26.so")); + stack.Install (nodes.Get (1)); + + // + // Assign the address 10.1.1.1 to the TCP implementation under test (index + // zero) and 10.1.1.2 to the reference implementation (index one). + // + Ipv4AddressHelper address; + address.SetBase ("10.1.1.0", "255.255.255.252"); + Ipv4InterfaceContainer interfaces = address.Assign (devices); + + // + // We need a place to send our TCP data on the node with the reference TCP + // implementation. We aren't really concerned about what happens there, so + // just create a sink. + // + uint16_t sinkPort = 8080; + Address sinkAddress (InetSocketAddress(interfaces.GetAddress (1), sinkPort)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); + ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1)); + sinkApps.Start (Seconds (0.)); + sinkApps.Stop (Seconds (1.1)); + + // + // We want to look at changes in the ns-3 TCP congestion window. The + // congestion window is flow clontrol imposed by the sender, so we need + // to crank up a flow from the ns-3 TCP node to the NSC TCP node and hook the + // CongestionWindow attribute on the socket. Normally one would use an on-off + // application to generate a flow, but this has a couple of problems. First, + // the socket of the on-off application is not created until Application Start + // time, so we wouldn't be able to hook the socket now at configuration time. + // Second, even if we could arrange a call after start time, the socket is not + // public. + // + // So, we can cook up a simple version of the on-off application that does what + // we want. On the plus side we don't need all of the complexity of the on-off + // application. On the minus side, we don't have a helper, so we have to get + // a little more involved in the details, but this is trivial. + // + // So first, we create a socket and do the trace connect on it; then we pass this + // socket into the constructor of our simple application which we then install + // in the node with the ns-3 TCP. + // + Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), TcpSocketFactory::GetTypeId ()); + ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&Ns3TcpCwndTestCase::CwndChange, this)); + + Ptr app = CreateObject (); + app->Setup (ns3TcpSocket, sinkAddress, 1040, 10, DataRate ("5Mbps")); + nodes.Get (0)->AddApplication (app); + app->Start (Seconds (1.)); + app->Stop (Seconds (1.1)); + + // + // The idea here is that someone will look very closely at the all of the + // communications between the reference TCP and the TCP under test in this + // simulation and determine that all of the responses are correct. We expect + // that this means generating a pcap trace file from the point-to-point link + // and examining the packets closely using tcpdump, wireshark or some such + // program. So we provide the ability to generate a pcap trace of the + // test execution for your perusal. + // + // Once the validation test is determined to be running exactly as exptected, + // the set of congestion window changes is collected and hard coded into the + // test results which will then be checked during the actual execution of the + // test. + // + + if (m_writeResults) + { + PointToPointHelper::EnablePcapAll ("tcp-cwnd"); + } + + Simulator::Stop (Seconds(2)); + Simulator::Run (); + Simulator::Destroy (); + + // + // As new acks are received by the TCP under test, the congestion window + // should be opened up by one segment (MSS bytes) each time. This should + // trigger a congestion window change event which we hooked and saved above. + // We should now be able to look through the saved response vectors and follow + // the congestion window as it opens up when the ns-3 TCP under test + // transmits its bits + // + // From inspecting the results, we know that we should see N_EVENTS congestion + // window change events. The window should expand N_EVENTS - 1 times (each + // time by MSS bytes) until it gets to its largest value. Then the application + // sending stops and the window should be slammed shut, with the last event + // reflecting the change from LARGEST_CWND back to MSS + // + const uint32_t MSS = 536; + const uint32_t N_EVENTS = 21; + const uint32_t LARGEST_CWND = MSS * N_EVENTS; + + CwndEvent event; + + NS_TEST_ASSERT_MSG_EQ (m_responses.GetN (), N_EVENTS, "Unexpectedly low number of cwnd change events"); + + + for (uint32_t i = 0, from = 536, to = 1072; i < N_EVENTS - 1; ++i, from += 536, to += 536) + { + event = m_responses.Get (i); + NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, from, "Wrong old cwnd value in cwnd change event " << i); + NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, to, "Wrong new cwnd value in cwnd change event " << i); + } + + event = m_responses.Get (N_EVENTS - 1); + NS_TEST_ASSERT_MSG_EQ (event.m_oldCwnd, LARGEST_CWND, "Wrong old cwnd value in cwnd change event " << N_EVENTS - 1); + NS_TEST_ASSERT_MSG_EQ (event.m_newCwnd, MSS, "Wrong new cwnd value in cwnd change event " << N_EVENTS - 1); + + return GetErrorStatus (); +} + +class Ns3TcpCwndTestSuite : public TestSuite +{ +public: + Ns3TcpCwndTestSuite (); +}; + +Ns3TcpCwndTestSuite::Ns3TcpCwndTestSuite () + : TestSuite ("ns3-tcp-cwnd", SYSTEM) +{ + AddTestCase (new Ns3TcpCwndTestCase); +} + +Ns3TcpCwndTestSuite ns3TcpCwndTestSuite; diff --git a/src/test/ns3tcp/ns3tcp-interop-response-vectors.pcap b/src/test/ns3tcp/ns3tcp-interop-response-vectors.pcap new file mode 100644 index 000000000..8280e07d1 Binary files /dev/null and b/src/test/ns3tcp/ns3tcp-interop-response-vectors.pcap differ diff --git a/src/test/ns3tcp/ns3tcp-interop-test-suite.cc b/src/test/ns3tcp/ns3tcp-interop-test-suite.cc new file mode 100644 index 000000000..c70dbd6fe --- /dev/null +++ b/src/test/ns3tcp/ns3tcp-interop-test-suite.cc @@ -0,0 +1,307 @@ +/* -*- 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 "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/test.h" +#include "ns3/pcap-file.h" +#include "ns3/config.h" +#include "ns3/string.h" +#include "ns3/uinteger.h" +#include "ns3/inet-socket-address.h" +#include "ns3/point-to-point-helper.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/ipv4-header.h" +#include "ns3/packet-sink-helper.h" +#include "ns3/on-off-helper.h" +#include "ns3/simulator.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("Ns3TcpInteropTest"); + +const bool WRITE_VECTORS = false; // hack hack hack +const uint32_t PCAP_LINK_TYPE = 1187373553; // Some large random number -- we use to verify data was written by this program +const uint32_t PCAP_SNAPLEN = 64; // Don't bother to save much data + + +// =========================================================================== +// This is a simple test to demonstrate how a known good model (a reference +// implementation) may be used to test another model in a relatively simple +// way. +// +// Node zero contains the model under test, in this case the ns-3 TCP +// implementation. Node one contains the reference implementation that we +// assume will generate good test vectors for us. In this case, a Linux +// TCP implementation is used to stimulate the ns-3 TCP model with what we +// assume are perfectly good packets. We watch the ns-3 implementation to +// see what it does in the presence of these assumed good stimuli. +// +// The test is arranged as a typical ns-3 script, but we use the trace system +// to peek into the running system and monitor the ns-3 TCP. +// +// The topology is just two nodes communicating over a point-to-point network. +// The point-to-point network is chosen because it is simple and allows us to +// easily generate pcap traces we can use to separately verify that the ns-3 +// implementation is responding correctly. Once the opration is verified, we +// capture a set of response vectors that are then checked in the test to +// ensure that the ns-3 TCP continues to respond correctly over time. +// +// node 0 node 1 +// +----------------+ +----------------+ +// | ns-3 TCP | | Linux TCP | +// +----------------+ +----------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +----------------+ +----------------+ +// | point-to-point | | point-to-point | +// +----------------+ +----------------+ +// | | +// +---------------------+ +// 5 Mbps, 2 ms +// +// =========================================================================== +class Ns3TcpInteroperabilityTestCase : public TestCase +{ +public: + Ns3TcpInteroperabilityTestCase (); + virtual ~Ns3TcpInteroperabilityTestCase (); + +private: + virtual void DoSetup (void); + virtual bool DoRun (void); + virtual void DoTeardown (void); + + void Ipv4L3Tx (std::string context, Ptr packet, uint32_t interfaceIndex); + + std::string m_pcapFilename; + PcapFile m_pcapFile; + bool m_writeVectors; +}; + +Ns3TcpInteroperabilityTestCase::Ns3TcpInteroperabilityTestCase () + : TestCase ("Check to see that the ns-3 TCP can work with liblinux2.6.26.so"), m_writeVectors(WRITE_VECTORS) +{ +} + +Ns3TcpInteroperabilityTestCase::~Ns3TcpInteroperabilityTestCase () +{ +} + +void +Ns3TcpInteroperabilityTestCase::DoSetup (void) +{ + // + // We expect there to be a file called tcp-interop-response-vectors.pcap" in + // the source directory of this file. + // + m_pcapFilename = NS_TEST_SOURCEDIR + "ns3tcp-interop-response-vectors.pcap"; + + if (m_writeVectors) + { + m_pcapFile.Open (m_pcapFilename, "w"); + m_pcapFile.Init(PCAP_LINK_TYPE, PCAP_SNAPLEN); + } + else + { + m_pcapFile.Open (m_pcapFilename, "r"); + NS_ABORT_MSG_UNLESS (m_pcapFile.GetDataLinkType () == PCAP_LINK_TYPE, "Wrong response vectors in directory"); + } +} + +void +Ns3TcpInteroperabilityTestCase::DoTeardown (void) +{ + m_pcapFile.Close (); +} + +void +Ns3TcpInteroperabilityTestCase::Ipv4L3Tx (std::string context, Ptr packet, uint32_t interfaceIndex) +{ + // + // We're not testing IP so remove and toss the header. In order to do this, + // though, we need to copy the packet since we have a const version. + // + Ptr p = packet->Copy (); + Ipv4Header ipHeader; + p->RemoveHeader (ipHeader); + + // + // What is left is the TCP header and any data that may be sent. We aren't + // sending any TCP data, so we expect what remains is only TCP header, which + // is a small thing to save. + // + if (m_writeVectors) + { + // + // Save the TCP under test response for later testing. + // + Time tNow = Simulator::Now (); + int64_t tMicroSeconds = tNow.GetMicroSeconds (); + m_pcapFile.Write (uint32_t (tMicroSeconds / 1000000), + uint32_t (tMicroSeconds % 1000000), + p->PeekData(), + p->GetSize ()); + } + else + { + // + // Read the TCP under test expected response from the expected vector + // file and see if it still does the right thing. + // + uint8_t expected[PCAP_SNAPLEN]; + uint32_t tsSec, tsUsec, inclLen, origLen, readLen; + m_pcapFile.Read (expected, sizeof(expected), tsSec, tsUsec, inclLen, origLen, readLen); + + uint8_t const *actual = p->PeekData(); + + uint32_t result = memcmp(actual, expected, readLen); + + // + // Avoid streams of errors -- only report the first. + // + if (GetErrorStatus () == false) + { + NS_TEST_EXPECT_MSG_EQ (result, 0, "Expected data comparison error"); + } + } +} + +bool +Ns3TcpInteroperabilityTestCase::DoRun (void) +{ + // + // Just create two nodes. One (node zero) will be the node with the TCP + // under test which is the ns-3 TCP implementation. The other node (node + // one) will be the node with the reference implementation we use to drive + // the tests. + // + NodeContainer nodes; + nodes.Create (2); + + // + // For this test we'll use a point-to-point net device. It's not as simple + // as a simple-net-device, but it provides nice places to hook trace events + // so we can see what's moving between our nodes. + // + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + + // + // Install the point-to-point devices on both nodes and connec them up. + // + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); + + // + // Install two variants of the internet stack. The first, on node zero + // uses the TCP under test, which is the default ns-3 TCP implementation. + // + InternetStackHelper stack; + stack.Install (nodes.Get (0)); + + // + // The other node, node one, is going to be set up to use a Linux TCP + // implementation that we consider a known good TCP. + // + std::string nscStack = "liblinux2.6.26.so"; + stack.SetTcp ("ns3::NscTcpL4Protocol", "Library", StringValue("liblinux2.6.26.so")); + stack.Install (nodes.Get (1)); + + // + // Assign the address 10.1.1.1 to the TCP implementation under test (index + // zero) and 10.1.1.2 to the reference implementation (index one). + // + Ipv4AddressHelper address; + address.SetBase ("10.1.1.0", "255.255.255.252"); + Ipv4InterfaceContainer interfaces = address.Assign (devices); + + // + // We need a place for the TCP data to go on the node with the TCP under + // test, so just create a sink on node zero. + // + uint16_t sinkPort = 8080; + Address sinkAddress (InetSocketAddress(interfaces.GetAddress (0), sinkPort)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); + ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (0)); + sinkApps.Start (Seconds (0.)); + + // + // We need something to shove data down the pipe, so we create an on-off + // application on the soure node with the reference TCP implementation. + // The default behavior is to send for one second, then go quiet for one + // second, and repeat. + // + OnOffHelper onOffHelper ("ns3::TcpSocketFactory", sinkAddress); + onOffHelper.SetAttribute ("MaxBytes", UintegerValue (100000)); + ApplicationContainer sourceApps = onOffHelper.Install(nodes.Get(1)); + sourceApps.Start (Seconds (1.)); + sourceApps.Stop (Seconds (10.)); + + // + // There are currently a limited number of trace hooks in the ns-3 TCP code. + // Rather than editing TCP to insert a bunch of trace hooks, we can just + // intercept the packets at the IPv4 layer. See internet-stack-helper.cc + // for complete description of the trace hooks. We're interested in the + // responses of the TCP under test, which implies we need to hook the node + // zero Ipv4 layer three transmit trace source. We'll then get all of the + // responses we need + // + Config::Connect ("/NodeList/0/$ns3::Ipv4L3Protocol/Tx", + MakeCallback (&Ns3TcpInteroperabilityTestCase::Ipv4L3Tx, this)); + + // + // The idea here is that someone will look very closely at the all of the + // communications between the reference TCP and the TCP under test in this + // simulation and determine that all of the responses are correct. We expect + // that this means generating a pcap trace file from the point-to-point link + // and examining the packets closely using tcpdump, wireshark or some such + // program. So we provide the ability to generate a pcap trace of the + // test execution for your perusal. + // + // Once the validation test is determined to be running exactly as exptected, + // we allow you to generate a file that contains the response vectors that + // will be checked during the actual execution of the test. + // + + if (m_writeVectors) + { + PointToPointHelper::EnablePcapAll ("tcp-interop"); + } + + Simulator::Stop (Seconds(20)); + Simulator::Run (); + Simulator::Destroy (); + + return GetErrorStatus (); +} + +class Ns3TcpInteroperabilityTestSuite : public TestSuite +{ +public: + Ns3TcpInteroperabilityTestSuite (); +}; + +Ns3TcpInteroperabilityTestSuite::Ns3TcpInteroperabilityTestSuite () + : TestSuite ("ns3-tcp-interoperability", SYSTEM) +{ + AddTestCase (new Ns3TcpInteroperabilityTestCase); +} + +Ns3TcpInteroperabilityTestSuite ns3TcpInteroperabilityTestSuite; diff --git a/src/test/ns3tcp/ns3tcp.h b/src/test/ns3tcp/ns3tcp.h new file mode 100644 index 000000000..30189c605 --- /dev/null +++ b/src/test/ns3tcp/ns3tcp.h @@ -0,0 +1,8 @@ +/** + * \ingroup tests + * \defgroup Ns3TcpTests ns-3 TCP Implementation Tests + * + * \section Ns3TcpTestsOverview ns-3 Tcp Implementation Tests Overview + * + * ns-3 has a TCP implemtation and we test it a litte. + */ diff --git a/src/test/ns3tcp/waf b/src/test/ns3tcp/waf new file mode 100644 index 000000000..4283ec141 --- /dev/null +++ b/src/test/ns3tcp/waf @@ -0,0 +1 @@ +exec "`dirname "$0"`"/../../../waf "$@" diff --git a/src/test/ns3tcp/wscript b/src/test/ns3tcp/wscript new file mode 100644 index 000000000..25549a4b7 --- /dev/null +++ b/src/test/ns3tcp/wscript @@ -0,0 +1,16 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def configure(conf): + pass + +def build(bld): + ns3tcp = bld.create_ns3_module('ns3tcp') + ns3tcp.source = [ + 'ns3tcp-interop-test-suite.cc', + 'ns3tcp-cwnd-test-suite.cc', + ] + headers = bld.new_task_gen('ns3header') + headers.module = 'ns3tcp' + headers.source = [ + 'ns3tcp.h', + ] diff --git a/src/test/ns3wifi/ns3wifi.h b/src/test/ns3wifi/ns3wifi.h new file mode 100644 index 000000000..d400eb530 --- /dev/null +++ b/src/test/ns3wifi/ns3wifi.h @@ -0,0 +1,8 @@ +/** + * \ingroup tests + * \defgroup Ns3WifiTests ns-3 Wifi Implementation Tests + * + * \section Ns3WifiTestsOverview ns-3 Wifi Implementation Tests Overview + * + * ns-3 has a Wifi implemtation and we test it a litte. + */ diff --git a/src/test/ns3wifi/propagation-loss-models-test-suite.cc b/src/test/ns3wifi/propagation-loss-models-test-suite.cc new file mode 100644 index 000000000..55f9a1bfd --- /dev/null +++ b/src/test/ns3wifi/propagation-loss-models-test-suite.cc @@ -0,0 +1,579 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2009 The Boeing Company + * + * 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 "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/test.h" +#include "ns3/pcap-file.h" +#include "ns3/config.h" +#include "ns3/string.h" +#include "ns3/uinteger.h" +#include "ns3/data-rate.h" +#include "ns3/inet-socket-address.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/tcp-socket-factory.h" +#include "ns3/yans-wifi-helper.h" +#include "ns3/propagation-loss-model.h" +#include "ns3/propagation-delay-model.h" +#include "ns3/yans-wifi-channel.h" +#include "ns3/yans-wifi-phy.h" +#include "ns3/wifi-net-device.h" +#include "ns3/mobility-helper.h" +#include "ns3/constant-position-mobility-model.h" +#include "ns3/nqos-wifi-mac-helper.h" +#include "ns3/simulator.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("Ns3WifiPropagationLossModelsTest"); + +// =========================================================================== +// This is a simple test to validate propagation loss models of ns-3 wifi. +// +// The basic configuration is, +// +// node 0 node 1 +// +------------+ +------------+ +// | ns-3 UDP | | ns-3 UDP | +// +------------+ +------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +------------+ +------------+ +// | wifi | | wifi | +// +------------+ +------------+ +// | | +// (((*))) (((*))) +// +// |<-- distance -->| +// +// +// We vary the propagation loss model and the distance between the nodes, +// looking at the received power and SNR for a packet sent between them. We +// compare the found values with values found in an "authoritative source." +// =========================================================================== +// +class Ns3FriisPropagationLossModelTestCase : public TestCase +{ +public: + Ns3FriisPropagationLossModelTestCase (); + virtual ~Ns3FriisPropagationLossModelTestCase (); + +private: + virtual bool DoRun (void); + + void SendPacket (uint32_t i, Ptr socket, uint32_t size); + void Receive (Ptr p, double snr, WifiMode mode, enum WifiPreamble preamble); + + uint32_t m_gotCallbacks; + + Ptr m_receiver; + uint32_t m_vectorIndex; + + typedef struct { + Vector m_position; + double m_snr; + double m_tolerance; + } TestVector; + + TestVectors m_testVectors; +}; + +Ns3FriisPropagationLossModelTestCase::Ns3FriisPropagationLossModelTestCase () + : TestCase ("Check to see that the ns-3 Friis propagation loss model provides correct SNR values"), + m_gotCallbacks (false), m_receiver (0), m_vectorIndex (0), m_testVectors () +{ +} + +Ns3FriisPropagationLossModelTestCase::~Ns3FriisPropagationLossModelTestCase () +{ +} + +void +Ns3FriisPropagationLossModelTestCase::Receive (Ptr p, double snr, WifiMode mode, enum WifiPreamble preamble) +{ + TestVector testVector = m_testVectors.Get (m_vectorIndex++); + if (GetErrorStatus () == false) + { + NS_TEST_EXPECT_MSG_EQ_TOL (snr, testVector.m_snr, testVector.m_tolerance, "Got unexpected SNR value"); + } + ++m_gotCallbacks; +} + +void +Ns3FriisPropagationLossModelTestCase::SendPacket (uint32_t i, Ptr socket, uint32_t size) +{ + TestVector testVector = m_testVectors.Get (i); + m_receiver->GetObject ()->SetPosition (testVector.m_position); + socket->Send (Create (size)); +} + +bool +Ns3FriisPropagationLossModelTestCase::DoRun (void) +{ + // + // We want to test the propagation loss model calculations at a few chosen + // distances and compare the results to those we have manually calculated + // according to the model documentation. The following "TestVector" objects + // will drive the test. + // + // For example, the first test vector provides a position to which the + // receiver node will be moved prior to transmitting a packet. It also + // specifies that when the packet is received, the SNR shold be found + // to be 1129.93 +- 0.005 in the ReceiveOkCallback. + // + TestVector testVector; + + testVector.m_position = Vector (100, 0, 0); + testVector.m_snr = 1128.93; + testVector.m_tolerance = 0.005; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (500, 0, 0); + testVector.m_snr = 45.1571; + testVector.m_tolerance = 0.00005; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (1000, 0, 0); + testVector.m_snr = 11.2893; + testVector.m_tolerance = 0.00005; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (2000, 0, 0); + testVector.m_snr = 2.82232; + testVector.m_tolerance = 0.000005; + m_testVectors.Add (testVector); + + // + // Disable fragmentation for frames shorter than 2200 bytes. + // + Config::SetDefault ("ns3::WifiRemoteStationManager::FragmentationThreshold", StringValue ("2200")); + + // + // Turn off RTS/CTS for frames shorter than 2200 bytes. + // + Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue ("2200")); + + // + // Create the two nodes in the system. Data will be sent from node zero to + // node one. + // + NodeContainer nodes; + nodes.Create (2); + + // + // Save a Ptr to the receiver node so we can get at its mobility model + // and change its position (distance) later. + // + m_receiver = nodes.Get (1); + + // + // Use the regular WifiHelper to orchestrate hooking the various pieces of + // the wifi system together. Tell it that we want to use an 802.11b phy. + // + WifiHelper wifi; + wifi.SetStandard (WIFI_PHY_STANDARD_80211b); + + // + // Create a physical layer helper and tell it we don't want any receiver + // gain. + // + YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); + wifiPhy.Set ("RxGain", DoubleValue (0) ); + + // + // Create the channel helper and tell it that signals will be moving at the + // speed of light. + // + YansWifiChannelHelper wifiChannel ; + wifiChannel.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel"); + + // + // The propagation loss model is one of our independent variables in the + // test. + // + wifiChannel.AddPropagationLoss ("ns3::FriisPropagationLossModel", + "Lambda", DoubleValue (0.125), + "SystemLoss", DoubleValue (1.)); + + // + // Create a yans wifi channel and tell the phy helper to use it. + // + wifiPhy.SetChannel (wifiChannel.Create ()); + + // + // Create a non-quality-of-service mac layer and set it to ad-hoc mode. + // + NqosWifiMacHelper wifiMac = NqosWifiMacHelper::Default (); + wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", + "DataMode", StringValue ("wifib-1mbs"), + "ControlMode",StringValue ("wifib-1mbs")); + wifiMac.SetType ("ns3::AdhocWifiMac"); + + // + // Create the wifi devices. + // + NetDeviceContainer devices = wifi.Install (wifiPhy, wifiMac, nodes); + + // + // We need to reach down into the receiving wifi device's phy layer and hook + // the appropriate trace event to get the snr. This isn't one of the usual + // events so it takes some poking around to get there from here. + // + Ptr phy = devices.Get (1)->GetObject ()->GetPhy ()->GetObject (); + phy->SetReceiveOkCallback (MakeCallback (&Ns3FriisPropagationLossModelTestCase::Receive, this)); + + // + // Add mobility models to both nodes. This is used to place the two nodes a + // fixed distance apart. Node zero (the sender) is always at the origin and + // Node one (the receiver) is moved along the x-axis to a given distance from + // the origin. This distance is the second independent variable in our test. + // + MobilityHelper mobility; + Ptr positionAlloc = CreateObject (); + positionAlloc->Add (Vector (0., 0., 0.)); + positionAlloc->Add (Vector (0, 0., 0.)); + mobility.SetPositionAllocator (positionAlloc); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (nodes); + + // + // In order to use UDP sockets, we need to install the ns-3 internet stack + // on our nodes. + // + InternetStackHelper internet; + internet.Install (nodes); + + // + // Assign IP addresses to our nodes. The source node is going to end up + // as 10.1.1.1 and the destination will be 10.1.1.2 + // + Ipv4AddressHelper addresses; + addresses.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer interfaces = addresses.Assign (devices); + + // + // The destination is the wifi device on node one. + // + InetSocketAddress destaddr = InetSocketAddress (interfaces.GetAddress (1), 80); + + // + // We just want to send packets from the source to the destination node, so + // the simplest thing is to cook something up manually. + // + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr dest = Socket::CreateSocket (nodes.Get (1), tid); + dest->Bind (destaddr); + + Ptr source = Socket::CreateSocket (nodes.Get (0), tid); + source->Connect (destaddr); + + // + // Schedule the packet sends, one packet per simulated second. + // + for (uint32_t i = 0; i < m_testVectors.GetN (); ++i) + { + Time t = Seconds (1. * i); + Simulator::Schedule (t, &Ns3FriisPropagationLossModelTestCase::SendPacket, this, i, source, 1000); + } + + Simulator::Stop (Seconds(1. * m_testVectors.GetN ())); + Simulator::Run (); + + source->Close (); + source = 0; + + dest->Close (); + dest = 0; + + m_receiver = 0; + + Simulator::Destroy (); + + // + // If we've already reported an error, just leave it at that. + // + if (GetErrorStatus () == false) + { + NS_TEST_ASSERT_MSG_EQ (m_gotCallbacks, m_testVectors.GetN (), "Did not get expected number of ReceiveOkCallbacks"); + } + + return GetErrorStatus (); +} + +class Ns3LogDistancePropagationLossModelTestCase : public TestCase +{ +public: + Ns3LogDistancePropagationLossModelTestCase (); + virtual ~Ns3LogDistancePropagationLossModelTestCase (); + +private: + virtual bool DoRun (void); + + void SendPacket (uint32_t i, Ptr socket, uint32_t size); + void Receive (Ptr p, double snr, WifiMode mode, enum WifiPreamble preamble); + + uint32_t m_gotCallbacks; + + Ptr m_receiver; + uint32_t m_vectorIndex; + + typedef struct { + Vector m_position; + double m_snr; + double m_tolerance; + } TestVector; + + TestVectors m_testVectors; +}; + +Ns3LogDistancePropagationLossModelTestCase::Ns3LogDistancePropagationLossModelTestCase () + : TestCase ("Check to see that the ns-3 Log Distance propagation loss model provides correct SNR values"), + m_gotCallbacks (false), m_receiver (0), m_vectorIndex (0), m_testVectors () +{ +} + +Ns3LogDistancePropagationLossModelTestCase::~Ns3LogDistancePropagationLossModelTestCase () +{ +} + +void +Ns3LogDistancePropagationLossModelTestCase::Receive (Ptr p, double snr, WifiMode mode, enum WifiPreamble preamble) +{ + TestVector testVector = m_testVectors.Get (m_vectorIndex++); + if (GetErrorStatus () == false) + { + NS_TEST_EXPECT_MSG_EQ_TOL (snr, testVector.m_snr, testVector.m_tolerance, "Got unexpected SNR value"); + } + ++m_gotCallbacks; +} + +void +Ns3LogDistancePropagationLossModelTestCase::SendPacket (uint32_t i, Ptr socket, uint32_t size) +{ + TestVector testVector = m_testVectors.Get (i); + m_receiver->GetObject ()->SetPosition (testVector.m_position); + socket->Send (Create (size)); +} + +bool +Ns3LogDistancePropagationLossModelTestCase::DoRun (void) +{ + // + // We want to test the propagation loss model calculations at a few chosen + // distances and compare the results to those we have manually calculated + // according to the model documentation. The following "TestVector" objects + // will drive the test. + // + // For example, the first test vector provides a position to which the + // receiver node will be moved prior to transmitting a packet. It also + // specifies that when the packet is received, the SNR shold be found + // to be 1129.93 +- 0.005 in the ReceiveOkCallback. + // + TestVector testVector; + + testVector.m_position = Vector (10, 0, 0); + testVector.m_snr = 11289.3; + testVector.m_tolerance = 0.05; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (20, 0, 0); + testVector.m_snr = 1411.16; + testVector.m_tolerance = 0.005; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (40, 0, 0); + testVector.m_snr = 176.407; + testVector.m_tolerance = 0.0005; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (80, 0, 0); + testVector.m_snr = 22.0494; + testVector.m_tolerance = 0.00005; + m_testVectors.Add (testVector); + + // + // Disable fragmentation for frames shorter than 2200 bytes. + // + Config::SetDefault ("ns3::WifiRemoteStationManager::FragmentationThreshold", StringValue ("2200")); + + // + // Turn off RTS/CTS for frames shorter than 2200 bytes. + // + Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue ("2200")); + + // + // Create the two nodes in the system. Data will be sent from node zero to + // node one. + // + NodeContainer nodes; + nodes.Create (2); + + // + // Save a Ptr to the receiver node so we can get at its mobility model + // and change its position (distance) later. + // + m_receiver = nodes.Get (1); + + // + // Use the regular WifiHelper to orchestrate hooking the various pieces of + // the wifi system together. Tell it that we want to use an 802.11b phy. + // + WifiHelper wifi; + wifi.SetStandard (WIFI_PHY_STANDARD_80211b); + + // + // Create a physical layer helper and tell it we don't want any receiver + // gain. + // + YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); + wifiPhy.Set ("RxGain", DoubleValue (0) ); + + // + // Create the channel helper and tell it that signals will be moving at the + // speed of light. + // + YansWifiChannelHelper wifiChannel ; + wifiChannel.SetPropagationDelay ("ns3::ConstantSpeedPropagationDelayModel"); + + // + // The propagation loss model is one of our independent variables in the + // test. + // + wifiChannel.AddPropagationLoss ("ns3::LogDistancePropagationLossModel", + "Exponent", DoubleValue(3), + "ReferenceLoss", DoubleValue(40.045997)); + + // + // Create a yans wifi channel and tell the phy helper to use it. + // + wifiPhy.SetChannel (wifiChannel.Create ()); + + // + // Create a non-quality-of-service mac layer and set it to ad-hoc mode. + // + NqosWifiMacHelper wifiMac = NqosWifiMacHelper::Default (); + wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", + "DataMode", StringValue ("wifib-1mbs"), + "ControlMode",StringValue ("wifib-1mbs")); + wifiMac.SetType ("ns3::AdhocWifiMac"); + + // + // Create the wifi devices. + // + NetDeviceContainer devices = wifi.Install (wifiPhy, wifiMac, nodes); + + // + // We need to reach down into the receiving wifi device's phy layer and hook + // the appropriate trace event to get the snr. This isn't one of the usual + // events so it takes some poking around to get there from here. + // + Ptr phy = devices.Get (1)->GetObject ()->GetPhy ()->GetObject (); + phy->SetReceiveOkCallback (MakeCallback (&Ns3LogDistancePropagationLossModelTestCase::Receive, this)); + + // + // Add mobility models to both nodes. This is used to place the two nodes a + // fixed distance apart. Node zero (the sender) is always at the origin and + // Node one (the receiver) is moved along the x-axis to a given distance from + // the origin. This distance is the second independent variable in our test. + // + MobilityHelper mobility; + Ptr positionAlloc = CreateObject (); + positionAlloc->Add (Vector (0., 0., 0.)); + positionAlloc->Add (Vector (0, 0., 0.)); + mobility.SetPositionAllocator (positionAlloc); + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (nodes); + + // + // In order to use UDP sockets, we need to install the ns-3 internet stack + // on our nodes. + // + InternetStackHelper internet; + internet.Install (nodes); + + // + // Assign IP addresses to our nodes. The source node is going to end up + // as 10.1.1.1 and the destination will be 10.1.1.2 + // + Ipv4AddressHelper addresses; + addresses.SetBase ("10.1.1.0", "255.255.255.0"); + Ipv4InterfaceContainer interfaces = addresses.Assign (devices); + + // + // The destination is the wifi device on node one. + // + InetSocketAddress destaddr = InetSocketAddress (interfaces.GetAddress (1), 80); + + // + // We just want to send packets from the source to the destination node, so + // the simplest thing is to cook something up manually. + // + TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory"); + Ptr dest = Socket::CreateSocket (nodes.Get (1), tid); + dest->Bind (destaddr); + + Ptr source = Socket::CreateSocket (nodes.Get (0), tid); + source->Connect (destaddr); + + // + // Schedule the packet sends, one packet per simulated second. + // + for (uint32_t i = 0; i < m_testVectors.GetN (); ++i) + { + Time t = Seconds (1. * i); + Simulator::Schedule (t, &Ns3LogDistancePropagationLossModelTestCase::SendPacket, this, i, source, 1000); + } + + Simulator::Stop (Seconds(1. * m_testVectors.GetN ())); + Simulator::Run (); + + source->Close (); + source = 0; + + dest->Close (); + dest = 0; + + m_receiver = 0; + + Simulator::Destroy (); + + // + // If we've already reported an error, just leave it at that. + // + if (GetErrorStatus () == false) + { + NS_TEST_ASSERT_MSG_EQ (m_gotCallbacks, m_testVectors.GetN (), "Did not get expected number of ReceiveOkCallbacks"); + } + + return GetErrorStatus (); +} + +class Ns3WifiPropagationLossModelsTestSuite : public TestSuite +{ +public: + Ns3WifiPropagationLossModelsTestSuite (); +}; + +Ns3WifiPropagationLossModelsTestSuite::Ns3WifiPropagationLossModelsTestSuite () + : TestSuite ("ns3-wifi-propagation-loss-models", SYSTEM) +{ + AddTestCase (new Ns3FriisPropagationLossModelTestCase); + AddTestCase (new Ns3LogDistancePropagationLossModelTestCase); +} + +Ns3WifiPropagationLossModelsTestSuite ns3WifiPropagationLossModelsTestSuite; diff --git a/src/test/ns3wifi/waf b/src/test/ns3wifi/waf new file mode 100644 index 000000000..4283ec141 --- /dev/null +++ b/src/test/ns3wifi/waf @@ -0,0 +1 @@ +exec "`dirname "$0"`"/../../../waf "$@" diff --git a/src/test/ns3wifi/wscript b/src/test/ns3wifi/wscript new file mode 100644 index 000000000..01dc8ab9f --- /dev/null +++ b/src/test/ns3wifi/wscript @@ -0,0 +1,15 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + +def configure(conf): + pass + +def build(bld): + ns3wifi = bld.create_ns3_module('ns3wifi') + ns3wifi.source = [ + 'propagation-loss-models-test-suite.cc', + ] + headers = bld.new_task_gen('ns3header') + headers.module = 'ns3wifi' + headers.source = [ + 'ns3wifi.h', + ] diff --git a/src/wscript b/src/wscript index 46de7f97a..da8d27117 100644 --- a/src/wscript +++ b/src/wscript @@ -36,8 +36,13 @@ all_modules = ( 'helper', 'contrib/stats', 'applications/v4ping', + 'devices/mesh', + 'devices/mesh/dot11s', + 'devices/mesh/flame', 'applications/ping6', 'applications/radvd', + 'test/ns3tcp', + 'test/ns3wifi', 'contrib/flow-monitor', ) @@ -59,7 +64,6 @@ def configure(conf): conf.sub_config('core') conf.sub_config('simulator') conf.sub_config('devices/emu') - conf.sub_config('devices/wifi') conf.sub_config('devices/tap-bridge') conf.sub_config('contrib') conf.sub_config('internet-stack') diff --git a/test.py b/test.py new file mode 100755 index 000000000..3aebc4c1c --- /dev/null +++ b/test.py @@ -0,0 +1,886 @@ +#! /usr/bin/env python +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- +# +# 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 +# + +import os +import sys +import optparse +import subprocess +import multiprocessing +import threading +import Queue +import signal +import random +import xml.dom.minidom + +# +# XXX This should really be part of a waf command to list the configuration +# items relative to optional ns-3 pieces. +# +# A list of interesting configuration items in the waf configuration +# cache which we may be interested in when deciding on which examples +# to run and how to run them. These are set by waf during the +# configuration phase and the corresponding assignments are usually +# found in the associated subdirectory wscript files. +# +interesting_config_items = [ + "NS3_BUILDDIR", + "NS3_MODULE_PATH", + "ENABLE_EMU", + "ENABLE_GSL", + "ENABLE_GTK_CONFIG_STORE", + "ENABLE_LIBXML2", + "ENABLE_NSC", + "ENABLE_PYTHON_BINDINGS", + "ENABLE_PYTHON_SCANNING", + "ENABLE_REAL_TIME", + "ENABLE_STATIC_NS3", + "ENABLE_SUDO", + "ENABLE_TAP", + "ENABLE_THREADING", +] + +# +# A list of examples to run as smoke tests just to ensure that they remain +# buildable and runnable over time. Also a condition under which to run +# the example (from the waf configuration). +# +# XXX Should this not be read from a configuration file somewhere and not +# hardcoded. +# +example_tests = [ + ("run-tests", "False"), + ("csma-bridge", "True"), + ("csma-bridge-one-hop", "True"), + ("csma-broadcast", "True"), + ("csma-multicast", "True"), + ("csma-one-subnet", "True"), + ("csma-packet-socket", "True"), + ("csma-ping", "True"), + ("csma-raw-ip-socket", "True"), + ("csma-star", "True"), + ("dynamic-global-routing", "True"), + ("first", "True"), + ("global-routing-slash32", "True"), + ("hello-simulator", "True"), + ("mixed-global-routing", "True"), + ("mixed-wireless", "True"), + ("object-names", "True"), + ("realtime-udp-echo", "ENABLE_REAL_TIME == True"), + ("second", "True"), + ("simple-alternate-routing", "True"), + ("simple-error-model", "True"), + ("simple-global-routing", "True"), + ("simple-point-to-point-olsr", "True"), + ("simple-wifi-frame-aggregation", "True"), + ("star", "True"), + ("static-routing-slash32", "True"), + ("tcp-large-transfer", "True"), + ("tcp-nsc-zoo", "ENABLE_NSC == True"), + ("tcp-star-server", "True"), + ("test-ipv6", "True"), + ("third", "True"), + ("udp-echo", "True"), + ("wifi-wired-bridging", "True"), +] + +# +# Most of the examples produce gangs of trace files, so we want to find +# somewhere to put them that won't pollute the current directory. One +# obvious place is somewhere in /tmp. +# +TMP_TRACES_DIR = "/tmp/unchecked-traces" + +# +# The test suites are going to want to output status. They are running +# concurrently. This means that unless we are careful, the output of +# the test suites will be interleaved. Rather than introducing a lock +# file that could unintentionally start serializing execution, we ask +# the tests to write their output to a temporary directory and then +# put together the final output file when we "join" the test tasks back +# to the main thread. +# +TMP_OUTPUT_DIR = "/tmp/testpy" + +def get_node_text(node): + for child in node.childNodes: + if child.nodeType == child.TEXT_NODE: + return child.nodeValue + return "None" + +# +# A simple example of writing a text file with a test result summary. +# +def translate_to_text(results_file, text_file): + f = open(text_file, 'w') + dom = xml.dom.minidom.parse(results_file) + for suite in dom.getElementsByTagName("TestSuite"): + result = get_node_text(suite.getElementsByTagName("SuiteResult")[0]) + name = get_node_text(suite.getElementsByTagName("SuiteName")[0]) + time = get_node_text(suite.getElementsByTagName("SuiteTime")[0]) + output = "%s: Test Suite \"%s\" (%s)\n" % (result, name, time) + f.write(output) + if result != "CRASH": + for case in suite.getElementsByTagName("TestCase"): + result = get_node_text(case.getElementsByTagName("CaseResult")[0]) + name = get_node_text(case.getElementsByTagName("CaseName")[0]) + time = get_node_text(case.getElementsByTagName("CaseTime")[0]) + output = " %s: Test Case \"%s\" (%s)\n" % (result, name, time) + f.write(output) + + if result == "FAIL": + f.write(" Details:\n") + f.write(" Message: %s\n" % get_node_text(case.getElementsByTagName("CaseMessage")[0])) + f.write(" Condition: %s\n" % get_node_text(case.getElementsByTagName("CaseCondition")[0])) + f.write(" Actual: %s\n" % get_node_text(case.getElementsByTagName("CaseActual")[0])) + f.write(" Limit: %s\n" % get_node_text(case.getElementsByTagName("CaseLimit")[0])) + f.write(" File: %s\n" % get_node_text(case.getElementsByTagName("CaseFile")[0])) + f.write(" Line: %s\n" % get_node_text(case.getElementsByTagName("CaseLine")[0])) + + for example in dom.getElementsByTagName("Example"): + result = get_node_text(example.getElementsByTagName("Result")[0]) + name = get_node_text(example.getElementsByTagName("Name")[0]) + output = "%s: Example \"%s\"\n" % (result, name) + f.write(output) + + f.close() + +# +# A simple example of writing an HTML file with a test result summary. +# +def translate_to_html(results_file, html_file): + f = open(html_file, 'w') + f.write("\n") + f.write("\n") + f.write("

    ns-3 Test Results

    \n") + + dom = xml.dom.minidom.parse(results_file) + + f.write("

    Test Suites

    \n") + for suite in dom.getElementsByTagName("TestSuite"): + name = get_node_text(suite.getElementsByTagName("SuiteName")[0]) + result = get_node_text(suite.getElementsByTagName("SuiteResult")[0]) + time = get_node_text(suite.getElementsByTagName("SuiteTime")[0]) + + if result == "PASS": + f.write("

    %s: %s (%s)

    \n" % (result, name, time)) + else: + f.write("

    %s: %s (%s)

    \n" % (result, name, time)) + + + f.write("\n") + f.write("\n") + + if result == "CRASH": + f.write("\n") + f.write("\n" % result) + f.write("\n") + f.write("
    Result
    %s
    \n") + continue + + f.write("Test Case Name\n") + f.write(" Time \n") + + if result == "FAIL": + f.write("Details\n") + + for case in suite.getElementsByTagName("TestCase"): + f.write("\n") + name = get_node_text(case.getElementsByTagName("CaseName")[0]) + result = get_node_text(case.getElementsByTagName("CaseResult")[0]) + time = get_node_text(case.getElementsByTagName("CaseTime")[0]) + if result == "FAIL": + f.write("%s\n" % result) + f.write("%s\n" % name) + f.write("%s\n" % time) + f.write("") + f.write("Message: %s, " % get_node_text(case.getElementsByTagName("CaseMessage")[0])) + f.write("Condition: %s, " % get_node_text(case.getElementsByTagName("CaseCondition")[0])) + f.write("Actual: %s, " % get_node_text(case.getElementsByTagName("CaseActual")[0])) + f.write("Limit: %s, " % get_node_text(case.getElementsByTagName("CaseLimit")[0])) + f.write("File: %s, " % get_node_text(case.getElementsByTagName("CaseFile")[0])) + f.write("Line: %s" % get_node_text(case.getElementsByTagName("CaseLine")[0])) + f.write("\n") + else: + f.write("%s\n" % result) + f.write("%s\n" % name) + f.write("%s\n" % time) + f.write("\n") + + f.write("\n") + f.write("\n") + + f.write("

    Examples

    \n") + f.write("\n") + f.write("\n") + f.write("\n") + for example in dom.getElementsByTagName("Example"): + f.write("\n") + result = get_node_text(example.getElementsByTagName("Result")[0]) + if result == "FAIL": + f.write("\n" % result) + else: + f.write("\n" % result) + name = get_node_text(example.getElementsByTagName("Name")[0]) + f.write("\n" % name) + f.write("\n") + + f.write("
    Result Example Name
    %s%s%s
    \n") + + f.write("\n") + f.write("\n") + f.close() + +# +# Python Control-C handling is broken in the presence of multiple threads. +# Signals get delivered to the runnable/running thread by default and if +# it is blocked, the signal is simply ignored. So we hook sigint and set +# a global variable telling the system to shut down gracefully. +# +thread_exit = False + +def sigint_hook(signal, frame): + global thread_exit + thread_exit = True + return 0 + +# +# Waf can be configured to compile in debug or optimized modes. In each +# case, the resulting built goes into a different directory. If we want +# test tests to run from the correct code-base, we have to figure out which +# mode waf is running in. This is called its active variant. +# +# XXX This function pokes around in the waf internal state file. To be a +# little less hacky, we should add a commmand to waf to return this info +# and use that result. +# +def read_waf_active_variant(): + for line in open("build/c4che/default.cache.py").readlines(): + if line.startswith("NS3_ACTIVE_VARIANT"): + exec(line, globals()) + break + + if options.verbose: + print "NS3_ACTIVE_VARIANT == %s" % NS3_ACTIVE_VARIANT + +# +# In general, the build process itself naturally takes care of figuring out +# which tests are built into the test runner. For example, if waf configure +# determines that ENABLE_EMU is false due to some missing dependency, +# the tests for the emu net device simply will not be built and will +# therefore not be included in the built test runner. +# +# Examples, however, are a different story. In that case, we are just given +# a list of examples that could be run. Instead of just failing, for example, +# nsc-tcp-zoo if NSC is not present, we look into the waf saved configuration +# for relevant configuration items. +# +# XXX This function pokes around in the waf internal state file. To be a +# little less hacky, we should add a commmand to waf to return this info +# and use that result. +# +def read_waf_config(): + for line in open("build/c4che/%s.cache.py" % NS3_ACTIVE_VARIANT).readlines(): + for item in interesting_config_items: + if line.startswith(item): + exec(line, globals()) + + if options.verbose: + for item in interesting_config_items: + print "%s ==" % item, eval(item) + +# +# It seems pointless to fork a process to run waf to fork a process to run +# the test runner, so we just run the test runner directly. The main thing +# that waf would do for us would be to sort out the shared library path but +# we can deal with that easily and do here. +# +# There can be many different ns-3 repositories on a system, and each has +# its own shared libraries, so ns-3 doesn't hardcode a shared library search +# path -- it is cooked up dynamically, so we do that too. +# +def make_library_path(): + global LIBRARY_PATH + + LIBRARY_PATH = "LD_LIBRARY_PATH='" + + if sys.platform == "darwin": + LIBRARY_PATH = "DYLD_LIBRARY_PATH='" + elif sys.platform == "win32": + LIBRARY_PATH = "PATH='" + elif sys.platform == "cygwin": + LIBRARY_PATH = "PATH='" + + for path in NS3_MODULE_PATH: + LIBRARY_PATH = LIBRARY_PATH + path + ":" + + LIBRARY_PATH = LIBRARY_PATH + "'" + +def run_job_synchronously(shell_command, directory): + cmd = "%s %s/%s/%s" % (LIBRARY_PATH, NS3_BUILDDIR, NS3_ACTIVE_VARIANT, shell_command) + if options.verbose: + print "Synchronously execute %s" % cmd + proc = subprocess.Popen(cmd, shell=True, cwd=directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout_results = proc.communicate()[0] + return (proc.returncode, stdout_results) + +# +# This class defines a unit of testing work. It will typically refer to +# a test suite to run using the test-runner, or an example to run directly. +# +class Job(): + def __init__(self): + self.is_break = False + self.is_example = False + self.shell_command = "" + self.display_name = "" + self.cwd = "" + self.tmp_file_name = "" + self.returncode = False + + # + # A job is either a standard job or a special job indicating that a worker + # thread should exist. This special job is indicated by setting is_break + # to true. + # + def set_is_break(self, is_break): + self.is_break = is_break + + # + # Examples are treated differently than standard test suites. This is + # mostly because they are completely unaware that they are being run as + # tests. So we have to do some special case processing to make them look + # like tests. + # + def set_is_example(self, is_example): + self.is_example = is_example + + # + # This is the shell command that will be executed in the job. For example, + # + # "utils/test-runner --suite=some-test-suite" + # + def set_shell_command(self, shell_command): + self.shell_command = shell_command + + # + # This is the dispaly name of the job, typically the test suite or example + # name. For example, + # + # "some-test-suite" or "udp-echo" + # + def set_display_name(self, display_name): + self.display_name = display_name + + # + # This is the base directory of the repository out of which the tests are + # being run. It will be used deep down in the testing framework to determine + # where the source directory of the test was, and therefore where to find + # provided test vectors. For example, + # + # "/home/user/repos/ns-3-dev" + # + def set_basedir(self, basedir): + self.basedir = basedir + + # + # This is the current working directory that will be given to an executing + # test as it is being run. It will be used for examples to tell them where + # to write all of the pcap files that we will be carefully ignoring. For + # example, + # + # "/tmp/unchecked-traces" + # + def set_cwd(self, cwd): + self.cwd = cwd + + # + # This is the temporary results file name that will be given to an executing + # test as it is being run. We will be running all of our tests in parallel + # so there must be multiple temporary output files. These will be collected + # into a single XML file at the end and then be deleted. The file names are + # just giant random numbers, for example + # + # "/tmp/testpy/5437925246732857" + # + def set_tmp_file_name(self, tmp_file_name): + self.tmp_file_name = tmp_file_name + + # + # The return code received when the job process is executed. + # + def set_returncode(self, returncode): + self.returncode = returncode + +# +# The worker thread class that handles the actual running of a given test. +# Once spawned, it receives requests for work through its input_queue and +# ships the results back through the output_queue. +# +class worker_thread(threading.Thread): + def __init__(self, input_queue, output_queue): + threading.Thread.__init__(self) + self.input_queue = input_queue + self.output_queue = output_queue + + def run(self): + while True: + job = self.input_queue.get() + # + # Worker threads continue running until explicitly told to stop with + # a special job. + # + if job.is_break: + return + # + # If the global interrupt handler sets the thread_exit variable, + # we stop doing real work and just report back a "break" in the + # normal command processing has happened. + # + if thread_exit == True: + job.set_is_break(True) + self.output_queue.put(job) + continue + # + # Otherwise go about the business of running tests as normal. + # + else: + if options.verbose: + print "Launch %s" % job.shell_command + + if job.is_example: + # + # If we have an example, the shell command is all we need to + # know. It will be something like "examples/udp-echo" + # + (job.returncode, standard_out) = run_job_synchronously(job.shell_command, job.cwd) + else: + # + # If we're a test suite, we need to provide a little more info + # to the test runner, specifically the base directory and temp + # file name + # + (job.returncode, standard_out) = run_job_synchronously(job.shell_command + " --basedir=%s --out=%s" % + (job.basedir, job.tmp_file_name), job.cwd) + + if options.verbose: + print standard_out + + self.output_queue.put(job) + +# +# This is the main function that does the work of interacting with the test-runner +# itself. +# +def run_tests(): + # + # Run waf to make sure that everything is built, configured and ready to go + # unless we are explicitly told not to. + # + if options.nowaf == False: + proc = subprocess.Popen("./waf", shell=True) + proc.communicate() + + # + # Pull some interesting configuration information out of waf, primarily + # so we can know where executables can be found, but also to tell us what + # pieces of the system have been built. This will tell us what examples + # are runnable. + # + read_waf_active_variant() + read_waf_config() + make_library_path() + + # + # There are a couple of options that imply we can to exit before starting + # up a bunch of threads and running tests. Let's detect these cases and + # handle them without doing all of the hard work. + # + if options.kinds: + (rc, standard_out) = run_job_synchronously("utils/test-runner --kinds", os.getcwd()) + print standard_out + + if options.list: + (rc, standard_out) = run_job_synchronously("utils/test-runner --list", os.getcwd()) + print standard_out + + if options.kinds or options.list: + return + + # + # We communicate results in two ways. First, a simple message relating + # PASS, FAIL, or SKIP is always written to the standard output. It is + # expected that this will be one of the main use cases. A developer can + # just run test.py with no options and see that all of the tests still + # pass. + # + # The second main use case is when detailed status is requested (with the + # --text or --html options). Typicall this will be text if a developer + # finds a problem, or HTML for nightly builds. In these cases, an + # XML file is written containing the status messages from the test suites. + # This file is then read and translated into text or HTML. It is expected + # that nobody will really be interested in the XML, so we write it to + # somewhere in /tmp with a random name to avoid collisions. Just in case + # some strange once-in-a-lifetime error occurs, we always write the info + # so it can be found, we just may not use it. + # + # When we run examples as smoke tests, they are going to want to create + # lots and lots of trace files. We aren't really interested in the contents + # of the trace files, so we also just stash them off in /tmp somewhere. + # + if not os.path.exists(TMP_OUTPUT_DIR): + os.makedirs(TMP_OUTPUT_DIR) + + if not os.path.exists(TMP_TRACES_DIR): + os.makedirs(TMP_TRACES_DIR) + + # + # Create the main output file and start filling it with XML. We need to + # do this since the tests will just append individual results to this file. + # + xml_results_file = TMP_OUTPUT_DIR + "%d.xml" % random.randint(0, sys.maxint) + f = open(xml_results_file, 'w') + f.write('\n') + f.write('\n') + f.close() + + # + # We need to figure out what test suites to execute. We are either given one + # suite or example explicitly via the --suite or --example option, or we + # need to call into the test runner and ask it to list all of the available + # test suites. Further, we need to provide the constraint information if it + # has been given to us. + # + # This translates into allowing the following options with respect to the + # suites + # + # ./test,py: run all of the suites + # ./test.py --constrain=unit: run all unit suites + # ./test,py --suite=some-test-suite: run the single suite + # ./test,py --example=udp-echo: run no test suites + # ./test,py --suite=some-suite --example=some-example: run the single suite + # + # We can also use the --constrain option to provide an ordering of test + # execution quite easily. + # + if len(options.suite): + suites = options.suite + "\n" + elif len(options.example) == 0: + if len(options.constrain): + (rc, suites) = run_job_synchronously("utils/test-runner --list --constrain=%s" % options.constrain, os.getcwd()) + else: + (rc, suites) = run_job_synchronously("utils/test-runner --list", os.getcwd()) + else: + suites = "" + + # + # suite_list will either a single test suite name that the user has + # indicated she wants to run or a list of test suites provided by + # the test-runner possibly according to user provided constraints. + # We go through the trouble of setting up the parallel execution + # even in the case of a single suite to avoid having two process the + # results in two different places. + # + suite_list = suites.split('\n') + + # + # We now have a possibly large number of test suites to run, so we want to + # run them in parallel. We're going to spin up a number of worker threads + # that will run our test jobs for us. + # + # XXX Need to figure out number of CPUs without the multiprocessing + # dependency since multiprocessing is not standard `till Python 2.6 + # + input_queue = Queue.Queue(0) + output_queue = Queue.Queue(0) + + jobs = 0 + threads=[] + + processors = multiprocessing.cpu_count() + for i in range(processors): + thread = worker_thread(input_queue, output_queue) + threads.append(thread) + thread.start() + + # + # We now have worker threads spun up, and a list of work to do. So, run + # through the list of test suites and dispatch a job to run each one. + # + # Dispatching will run with unlimited speed and the worker threads will + # execute as fast as possible from the queue. + # + for test in suite_list: + if len(test): + job = Job() + job.set_is_example(False) + job.set_display_name(test) + job.set_tmp_file_name(TMP_OUTPUT_DIR + "%d" % random.randint(0, sys.maxint)) + job.set_cwd(os.getcwd()) + job.set_basedir(os.getcwd()) + job.set_shell_command("utils/test-runner --suite='%s'" % test) + + if options.verbose: + print "Queue %s" % test + + input_queue.put(job) + jobs = jobs + 1 + + # + # We've taken care of the discovered or specified test suites. Now we + # have to deal with examples run as smoke tests. We have a list of all of + # the example programs it makes sense to try and run. Each example will + # have a condition associated with it that must evaluate to true for us + # to try and execute it. This is used to determine if the example has + # a dependency that is not satisfied. For example, if an example depends + # on NSC being configured by waf, that example should have a condition + # that evaluates to true if NSC is enabled. For example, + # + # ("tcp-nsc-zoo", "ENABLE_NSC == True"), + # + # In this case, the example "tcp-nsc-zoo" will only be run if we find the + # waf configuration variable "ENABLE_NSC" to be True. + # + # We don't care at all how the trace files come out, so we just write them + # to a single temporary directory. + # + # XXX As it stands, all of the trace files have unique names, and so file + # collisions can only happen if two instances of an example are running in + # two versions of the test.py process concurrently. We may want to create + # uniquely named temporary traces directories to avoid this problem. + # + # We need to figure out what examples to execute. We are either given one + # suite or example explicitly via the --suite or --example option, or we + # need to walk the list of examples looking for available example + # conditions. + # + # This translates into allowing the following options with respect to the + # suites + # + # ./test,py: run all of the examples + # ./test.py --constrain=unit run no examples + # ./test.py --constrain=example run all of the examples + # ./test,py --suite=some-test-suite: run no examples + # ./test,py --example=some-example: run the single example + # ./test,py --suite=some-suite --example=some-example: run the single example + # + # XXX could use constrain to separate out examples used for performance + # testing + # + if len(options.suite) == 0 and len(options.example) == 0: + if len(options.constrain) == 0 or options.constrain == "example": + for test, condition in example_tests: + if eval(condition) == True: + job = Job() + job.set_is_example(True) + job.set_display_name(test) + job.set_tmp_file_name("") + job.set_cwd(TMP_TRACES_DIR) + job.set_basedir(os.getcwd()) + job.set_shell_command("examples/%s" % test) + + if options.verbose: + print "Queue %s" % test + + input_queue.put(job) + jobs = jobs + 1 + elif len(options.example): + # + # If you tell me to run an example, I will try and run the example + # irrespective of any condition. + # + job = Job() + job.set_is_example(True) + job.set_display_name(options.example) + job.set_tmp_file_name("") + job.set_cwd(TMP_TRACES_DIR) + job.set_basedir(os.getcwd()) + job.set_shell_command("examples/%s" % options.example) + + if options.verbose: + print "Queue %s" % test + + input_queue.put(job) + jobs = jobs + 1 + + # + # Tell the worker threads to pack up and go home for the day. Each one + # will exit when they see their is_break task. + # + for i in range(processors): + job = Job() + job.set_is_break(True) + input_queue.put(job) + + # + # Now all of the tests have been dispatched, so all we have to do here + # in the main thread is to wait for them to complete. Keyboard interrupt + # handling is broken as mentioned above. We use a signal handler to catch + # sigint and set a global variable. When the worker threads sense this + # they stop doing real work and will just start throwing jobs back at us + # with is_break set to True. In this case, there are no real results so we + # ignore them. If there are real results, we always print PASS or FAIL to + # standard out as a quick indication of what happened. + # + for i in range(jobs): + job = output_queue.get() + if job.is_break: + continue + + if job.is_example: + kind = "Example" + else: + kind = "TestSuite" + + if job.returncode == 0: + status = "PASS" + else: + status = "FAIL" + + print "%s: %s %s" % (status, kind, job.display_name) + + if job.is_example == True: + # + # Examples are the odd man out here. They are written without any + # knowledge that they are going to be run as a test, so we need to + # cook up some kind of output for them. We're writing an xml file, + # so we do some simple XML that says we ran the example. + # + # XXX We could add some timing information to the examples, i.e. run + # them through time and print the results here. + # + f = open(xml_results_file, 'a') + f.write('\n') + example_name = " %s\n" % job.display_name + f.write(example_name) + if job.returncode == 0: + f.write(' PASS\n') + elif job.returncode == 1: + f.write(' FAIL\n') + else: + f.write(' CRASH\n') + + f.write('\n') + f.close() + else: + # + # If we're not running an example, we're running a test suite. + # These puppies are running concurrently and generating output + # that was written to a temporary file to avoid collisions. + # + # Now that we are executing sequentially in the main thread, we can + # concatenate the contents of the associated temp file to the main + # results file and remove that temp file. + # + # One thing to consider is that a test suite can crash just as + # well as any other program, so we need to deal with that + # possibility as well. If it ran correctly it will return 0 + # if it passed, or 1 if it failed. In this case, we can count + # on the results file it saved being complete. If it crashed, it + # will return some other code, and the file should be considered + # corrupt and useless. If the suite didn't create any XML, then + # we're going to have to do it ourselves. + # + if job.returncode == 0 or job.returncode == 1: + f_to = open(xml_results_file, 'a') + f_from = open(job.tmp_file_name, 'r') + f_to.write(f_from.read()) + f_to.close() + f_from.close() + else: + f = open(xml_results_file, 'a') + f.write("\n") + f.write(" %s\n" % job.display_name) + f.write(' CRASH\n') + f.write("\n") + f.close() + + os.remove(job.tmp_file_name) + + # + # We have all of the tests run and the results written out. One final + # bit of housekeeping is to wait for all of the threads to close down + # so we can exit gracefully. + # + for thread in threads: + thread.join() + + # + # Back at the beginning of time, we started the body of an XML document + # since the test suites and examples were going to just write their + # individual pieces. So, we need to finish off and close out the XML + # document + # + f = open(xml_results_file, 'a') + f.write('\n') + f.close() + + # + # The last things to do are to translate the XML results file to "human + # readable form" if the user asked for it + # + if len(options.html): + translate_to_html(xml_results_file, options.html) + + if len(options.text): + translate_to_text(xml_results_file, options.text) + +def main(argv): + random.seed() + + parser = optparse.OptionParser() + parser.add_option("-c", "--constrain", action="store", type="string", dest="constrain", default="", + metavar="KIND", + help="constrain the test-runner by kind of test") + + parser.add_option("-e", "--example", action="store", type="string", dest="example", default="", + metavar="EXAMPLE", + help="specify a single example to run") + + parser.add_option("-k", "--kinds", action="store_true", dest="kinds", default=False, + help="print the kinds of tests available") + + parser.add_option("-l", "--list", action="store_true", dest="list", default=False, + help="print the list of known tests") + + parser.add_option("-n", "--nowaf", action="store_true", dest="nowaf", default=False, + help="do not run waf before starting testing") + + parser.add_option("-s", "--suite", action="store", type="string", dest="suite", default="", + metavar="TEST-SUITE", + help="specify a single test suite to run") + + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, + help="print progress and informational messages") + + parser.add_option("-w", "--web", "--html", action="store", type="string", dest="html", default="", + metavar="HTML-FILE", + help="write detailed test results into HTML-FILE.html") + + parser.add_option("-t", "--text", action="store", type="string", dest="text", default="", + metavar="TEXT-FILE", + help="write detailed test results into TEXT-FILE.txt") + + global options + options = parser.parse_args()[0] + signal.signal(signal.SIGINT, sigint_hook) + run_tests() + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/utils/test-runner.cc b/utils/test-runner.cc new file mode 100644 index 000000000..d12118c46 --- /dev/null +++ b/utils/test-runner.cc @@ -0,0 +1,248 @@ +/* -*- 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 "ns3/test.h" + +#include +#include +#include + +using namespace ns3; + +// +// Run one of the test suites. Returns an integer with the boolean sense of +// "an error has occurred." That is, 0 == false -> no error; 1 == true -> an +// error occurred. +// +int +main (int argc, char *argv[]) +{ + bool doVerbose = false; + bool doList = false; + bool doHelp = false; + bool doSuite = false; + bool doKinds = false; + + bool haveBasedir = false; + bool haveOutfile = false; + bool haveType = false; + + std::string suiteName; + std::string basedir; + std::string outfileName; + std::string typeName; + + for (int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + + if (arg.find ("--basedir=") != std::string::npos) + { + basedir = arg.substr (arg.find_first_of ("=") + 1, 9999); + haveBasedir = true; + } + + if (arg.find ("--constrain=") != std::string::npos) + { + typeName = arg.substr (arg.find_first_of ("=") + 1, 9999); + haveType = true; + } + + if (arg.compare ("--help") == 0) + { + doHelp = true; + } + + if (arg.compare ("--kinds") == 0) + { + doKinds = true; + } + + if (arg.compare ("--list") == 0) + { + doList = true; + } + + if (arg.find ("--out=") != std::string::npos) + { + outfileName = arg.substr (arg.find_first_of ("=") + 1, 9999); + haveOutfile = true; + } + + if (arg.find ("--suite=") != std::string::npos) + { + suiteName = arg.substr (arg.find_first_of ("=") + 1, 9999); + doSuite = true; + } + + if (arg.compare ("--verbose") == 0) + { + doVerbose = true; + } + } + + // + // A help request trumps everything else. If we have one, just print the help + // and leave. + // + if (doHelp) + { + std::cout << " --basedir=dir: Set the base directory (where to find src) to \"dir\"" << std::endl; + std::cout << " --constrain=test-type: Constrain checks to test suites of type \"test-type\"" << std::endl; + std::cout << " --help: Print this message" << std::endl; + std::cout << " --kinds: List all of the available kinds of tests" << std::endl; + std::cout << " --list: List all of the test suites (optionally constrained by test-type)" << std::endl; + std::cout << " --out=file-name: Set the test status output file to \"file-name\"" << std::endl; + std::cout << " --suite=suite-name: Run the test suite named \"suite-name\"" << std::endl; + std::cout << " --verbose: Turn on messages in the run test suites" << std::endl; + + return false; + } + + // + // A kinds request trumps everything remaining. If we are asked, just + // print the list of types and leave. + // + if (doKinds) + { + // + // Coming up with a string to represent a test type is completely up to + // us here. We just define the types as being a string composed of the + // enum defined in test.h converted to lower case. + // + std::cout << " bvt: Build Verification Tests (to see if build completed successfully)" << std::endl; + std::cout << " unit: Unit Tests (within modules to check basic functionality)" << std::endl; + std::cout << " system: System Tests (spans modules to check integration of modules)" << std::endl; + std::cout << " example: Examples (to see if example programs run successfully)" << std::endl; + std::cout << " performance: Performance Tests (check to see if the system is as fast as expected)" << std::endl; + + return false; + } + + // + // A list request is the first functional request. It trumps running the + // actual tests. If we get a list request, we don't run anything, we just + // do the requested list which may or may not be qualified by a typename. + // + if (doList) + { + for (uint32_t i = 0; i < TestRunner::GetNTestSuites (); ++i) + { + TestSuite *suite = TestRunner::GetTestSuite (i); + + // + // Filter the tests listed by type if requested. + // + if (haveType) + { + TestSuite::TestType type = suite->GetTestType (); + if (typeName == "bvt" && type != TestSuite::BVT) + { + continue; + } + + if (typeName == "unit" && type != TestSuite::UNIT) + { + continue; + } + + if (typeName == "system" && type != TestSuite::SYSTEM) + { + continue; + } + + if (typeName == "example" && type != TestSuite::EXAMPLE) + { + continue; + } + + if (typeName == "performance" && type != TestSuite::PERFORMANCE) + { + continue; + } + } + + // + // This creates a list of test suite names that can be used by the + // high level test manager to get a list of all tests. It will then + // typically launch individual tests in parallel, calling back here + // with a specific "suite=" to run. + // + std::cout << suite->GetName () << std::endl; + } + + return false; + } + + // + // If we haven't been asked to run a test suite, we are just going to happily + // try and run everything. Test suites are possibly going to need to figure + // out where there source directory is, and to do that they will need to know + // where the base directory of the distribution is (the directory in which + // "src" is found). We could try and run without it, but when it is needed, + // the test will fail with an assertion. So to be safe, we require a basedir + // to proceed. + // + + if (haveBasedir == false) + { + std::cout << "Must specify a base directory to run tests (use --basedir option)" << std::endl; + return true; + } + + // + // If given an output file, we just append the output of each test suite + // we're asked to run to the end of that file. We need to append since the + // higher level test runner may be just running a number of tests back to + // back. We leave it up to that code to decide how to deal with possible + // parallel operation -- we just append to a file here. If no output file + // is specified, we don't do any output and just return the sense of error + // given by the test. + // + std::ofstream *pofs = 0; + std::ofstream ofs; + + if (!outfileName.empty ()) + { + ofs.open (outfileName.c_str (), std::fstream::out | std::fstream::app); + pofs = &ofs; + } + + // + // If we have a specified test suite to run, then we only run that suite. + // The default case is to "run everything. We don't expect this to be done + // much since typically higher level code will be running suites in parallel + // but we'll do it if asked. + // + bool result = false; + + for (uint32_t i = 0; i < TestRunner::GetNTestSuites (); ++i) + { + TestSuite *testSuite = TestRunner::GetTestSuite (i); + if (doSuite == false || (doSuite == true && suiteName == testSuite->GetName ())) + { + testSuite->SetBaseDir (basedir); + testSuite->SetStream (pofs); + testSuite->SetVerbose (doVerbose); + result |= testSuite->Run (); + } + } + + ofs.close(); + return result; +} diff --git a/utils/wscript b/utils/wscript index 389cc25f8..d30726465 100644 --- a/utils/wscript +++ b/utils/wscript @@ -9,6 +9,11 @@ def build(bld): unit_tests.source = 'run-tests.cc' ## link unit test program with all ns3 modules unit_tests.uselib_local = 'ns3' + + test_runner = bld.create_ns3_program('test-runner', ['core']) + test_runner.install_path = None # do not install + test_runner.source = 'test-runner.cc' + test_runner.uselib_local = 'ns3' obj = bld.create_ns3_program('bench-simulator', ['simulator']) obj.source = 'bench-simulator.cc' diff --git a/wscript b/wscript index 8c360304a..80603820b 100644 --- a/wscript +++ b/wscript @@ -141,6 +141,11 @@ def set_options(opt): help=('Run doxygen to generate html documentation from source comments'), action="store_true", default=False, dest='doxygen') + opt.add_option('--doxygen-no-build', + help=('Run doxygen to generate html documentation from source comments, ' + 'but do not wait for ns-3 to finish the full build.'), + action="store_true", default=False, + dest='doxygen_no_build') opt.add_option('--run', help=('Run a locally built program; argument can be a program name,' @@ -353,8 +358,15 @@ def configure(conf): else: conf.report_optional_feature("static", "Static build", False, "option --enable-static not selected") + have_gsl = conf.pkg_check_modules('GSL', 'gsl', mandatory=False) + conf.env['ENABLE_GSL'] = have_gsl - + conf.report_optional_feature("GSL", "GNU Scientific Library (GSL)", + conf.env['ENABLE_GSL'], + "GSL not found") + if have_gsl: + conf.env.append_value('CXXDEFINES', "ENABLE_GSL") + conf.env.append_value('CCDEFINES', "ENABLE_GSL") # Write a summary of optional features status print "---- Summary of optional NS-3 features:" @@ -555,9 +567,14 @@ def build(bld): regression.run_regression(bld, regression_traces) if Options.options.check: - Options.options.compile_targets += ',run-tests,ns3module' + Options.options.compile_targets += ',run-tests' + if env['ENABLE_PYTHON_BINDINGS']: + Options.options.compile_targets += ',ns3module' _run_check(bld) + if Options.options.doxygen_no_build: + doxygen() + raise SystemExit(0) def shutdown(ctx): bld = wutils.bld