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/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/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/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/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 82d757639..9bec88fbe 100644 --- a/src/wscript +++ b/src/wscript @@ -41,6 +41,8 @@ all_modules = ( 'devices/mesh/flame', 'applications/ping6', 'applications/radvd', + 'test/ns3tcp', + 'test/ns3wifi', ) def set_options(opt): @@ -61,7 +63,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 4f26c9f67..80603820b 100644 --- a/wscript +++ b/wscript @@ -358,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:"