ns-3 network simulator tutorial. Chapter 5

ns-3 network simulator tutorial. Chapter 5
chapter 1,2
chapter 3
chapter 4

5 Setting
5.1 Using the logging module
5.1.1 Overview of logging
5.1.2 Enable logging
5.1.3 Adding logging to your code
5.2 Using command line arguments
5.2.1 Overriding default attribute values
5.2.2 Capturing your own commands
5.3 Using the trace system
5.3.1 ASCII Tracing
Parsing ASCII traces
5.3.2 PCAP trace

Chapter 5

Setting

5.1 Using the logging module

We have already briefly covered the ns-3 logging module by looking at the script first.cc. In this chapter, we'll take a closer look at the possible use cases for the logging subsystem.

5.1.1 Overview of logging

Many large systems support some sort of message logging facility, and ns-3 is no exception. In some cases, only error messages are written to the "operator's console" (which is usually stderr on Unix-based systems). On other systems, warning messages may be displayed as well as more detailed information. In some cases, loggers are used to print debug messages that can quickly blur the output.

The subchip used in ns-3 assumes that all of these levels of information are useful, and we provide a selective, layered approach to message logging. Logging can be completely disabled, enabled for individual components, or globally. For this, customizable levels of information content are used. The ns-3 logging module provides a relatively easy way to get useful information from your simulation.

You should understand that we provide a general purpose mechanism - tracing - for extracting data from your models, which should be the preferred output when modeling (for more information about our tracing system, see tutorial section 5.3). Logging should be the preferred method for getting debugging information, warnings, error messages, or for quickly outputting messages from your scripts or models at any time.

Currently, the system defines seven levels (types) of log messages in ascending order of informativeness.

  • LOG_ERROR Logging error messages (associated macro: NS_LOG_ERROR)
  • LOG_WARN Logging warning messages (associated macro: NS_LOG_WARN)
  • LOG_DEBUG Log relatively rare special debug messages (associated macro: NS_LOG_DEBUG)
  • LOG_INFO - registration of information messages about the progress of the program (associated macro: NS_LOG_INFO);
  • LOG_FUNCTION Logging messages describing each function called (two related macros: NS_LOG_FUNCTION used for member functions and NS_LOG_FUNCTION_NOARGS used for static functions)
  • LOG_LOGIC - logging messages describing the logical flow within the function (associated macro: NS_LOG_LOGIC);
  • LOG_ALL - logging everything mentioned above (there is no associated macro).
    For each type (LOG_TYPE) there is also a LOG_LEVEL_TYPE which, if used, allows all levels above it to be logged in addition to its own level. (As a consequence, LOG_ERROR and LOG_LEVEL_ERROR, and LOG_ALL and LOG_LEVEL_ALL are functionally equivalent.) For example, enabling LOG_INFO will only enable messages provided by the NS_LOG_INFO macro, enabling LOG_LEVEL_INFO will also enable messages provided by the NS_LOG_DEBUG, NS_LOG_WARN, and NS_LOG_ERROR macros.

We also provide an unconditional logging macro that is always displayed, regardless of the logging level or component selection.

  • NS_LOG_UNCOND Unconditional logging of the associated message (no associated logging level).

Each level can be requested individually or cumulatively. Logging can be configured using the NS_LOG sh environment variable or by logging a system function call. As shown earlier, the logging system has Doxygen documentation and now is a good time to review it if you haven't already.

Now that you have read the documentation in great detail, let's use this knowledge to get some interesting information from the example script scratch/myfirst.ccwhich you have already compiled.

5.1.2 Enable logging

Let's use the NS_LOG environment variable to start some more logs, but first, just to get our bearings, run the last script as you did before,

$ ./waf --run scratch/myfirst

You should see the familiar output of the first ns-3 example program

$ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build'
finished successfully (0.413s)
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

It turns out that the "sent" and "received" messages you see above are actually logged messages from UdpEchoClientApplication ΠΈ UdpEchoServerApplication. For example, we can ask the client application to print additional information by setting its logging level through the NS_LOG environment variable.

From now on, I'm going to assume that you're using a sh-like shell that uses the "VARIABLE=value" syntax. If you're using a csh-like shell, then you'll have to convert my examples to the "setenv variable value" syntax required by those shells.

Currently, the UDP echo client application responds to the following line of code in scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

It activates the LOG_LEVEL_INFO logging level. When we pass a logging level flag, we actually enable that level and all lower levels. In this case, we have included NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN and NS_LOG_ERROR. We can increase the logging level and get more information, without changing the script and recompiling, by setting the NS_LOG environment variable like this:

$ export NS_LOG=UdpEchoClientApplication=level_all

This is how we set the sh-shell's NS_LOG variable to the following value,

UdpEchoClientApplication=level_all

The left side of the assignment is the name of the logged bean we want to set up, and the right side is the flag we want to use to do so. In this case, we are going to enable all levels of debugging in the application. If you run the script with NS_LOG set this way, the ns-3 logging system will accept the changes and you should see the following output:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Additional debug information provided by the application now matches the NS_LOG_FUNCTION level. It shows every instance of a function call during script execution. As a rule, in method functions it is preferable to use (at least)NS_LOG_FUNCTION (this)... Use NS_LOG_FUNCTION_NOARGS ()
only in static functions. However, note that the ns-3 system is not required to support any logging functionality. The decision on how much information is recorded is up to the individual modeler. In the case of echo applications, a large amount of logging output is available.

Now you can view the log of function calls that were made by the application. If you look closely, you will notice a colon between the line UdpEchoClientApplication and the method name, where you might expect to see the C++ scope operator (::). This is done on purpose.

This is not actually a class name, but the name of a logging component. When there is a match between a source file and a class, it is usually the name of the class, but you must be aware that it is not really the name of the class, and there is a single colon instead of a double colon. This is a way to help you conceptually separate the logging bean name from the class name in a relatively subtle way.

However, in some cases it can be difficult to determine which method is actually generating the log message. If you look at the text above, you will be wondering where the line "Received 1024 bytes from 10.1.1.2". You can solve this problem by setting the level prefix_func to the NS_LOG environment variable. Try to do the following,

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Note that the quotes are necessary because the pipe we use to indicate the OR operation is also a Unix pipeline connector. Now, if you run the script, you will see that the logging system ensures that every message in this log is prefixed with the component name.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

You can now see that all messages coming from the UDP echo client application are identified as such. Message "Received 1024 bytes from 10.1.1.2' is now clearly identified as coming from the echo client application. The remaining message must come from the UDP echo server application. We can enable this component by entering a colon-separated list of components in the NS_LOG environment variable.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

Warning: In the example text above, you will need to remove the newline character after the colon (:), which is used to format the document. Now if you run the script, you will see all the log messages from the client and server echo applications. You can see that this can be very helpful when debugging.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
UdpEchoServerApplication:HandleRead(): Echoing packet
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

It is also sometimes useful to be able to see the simulation time at which the log message was created. You can do this by adding an OR bit prefix_time:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'

Again, you will have to remove the newline character above. If you now run the script, you should see the following output:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Note that the constructor for UdpEchoServer was called during simulation 0 seconds. This actually happens before the simulation starts, but this time is displayed as zero seconds. The same is true for constructor message UdpEchoClient.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Recall that the script scratch/first.cc started the echo server application one second before the simulation started. Now you can see that the method start application server is actually called in the first second. You may also notice that the echo client starts on the second second of the simulation, as we requested in the script.

Now you can follow the simulation progress on call ScheduleTransmit in the client that calls the Send callback of the HandleRead in the echo server application. Note that the elapsed time to send a packet over a point-to-point link is 3,69 milliseconds. You can see that the echo server logs a message that it has responded to the packet, and then, after the channel is delayed, you see that the echo client receives the echo packet in its HandleRead method.

There is a lot going on in this simulation without you noticing. But you can track the whole process very easily by enabling all the logging components in the system. Try setting the NS_LOG variable to the following value,

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

The asterisk above is a logging component wildcard. This will enable all entries in all components used in the simulation. I won't reproduce the output here (at the time of writing it produces 1265 lines of output for a single echo packet), but you can redirect this information to a file and view it with your favorite editor.

$ ./waf --run scratch/myfirst > log.out 2>&1

I personally use this extremely verbose version of logging when I have a problem and have no idea where things went wrong. I can follow code execution pretty easily without having to set breakpoints and step through the code in the debugger. I can just edit the output in my favorite editor and look for what I'm expecting and see what happens that I didn't expect. Once I have a general idea of ​​what is going wrong, I jump into the debugger to examine the problem in detail. This kind of output can be especially useful when your script does something completely unexpected. If you're only using the debugger, you might miss the twist entirely. Registration makes such turns noticeable.

5.1.3 Adding logging to your code

You can add new entries to your simulations by making calls to the log component from multiple macros. Let's do it in a script myfirst.cc, which we have in the "clean" directory. Recall that we have defined a logging component in this scenario:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

You know that you can enable logging of all messages for this component by setting the NS_LOG environment variable at various levels. Let's go ahead and add some entries to the script. The macro used to add information level messages to the log is NS_LOG_INFO. Let's add a message (just before we start creating nodes) that tells you that the script is in the "Creating Topology" phase. This is done in the following code snippet,
Open scratch/myfirst.cc in your favorite editor and add the line,
NS_LOG_INFO ("Creating Topology");
right before the lines,

NodeContainer nodes;
nodes.Create (2);

Now compile the script using WAF, and clear the NS_LOG variable to disable the logging thread we enabled earlier:

$ ./waf
$ export NS_LOG=
Π’Π΅ΠΏΠ΅Ρ€ΡŒ, Ссли Π²Ρ‹ запуститС скрипт,
$ ./waf --run scratch/myfirst

you will not see the new message because its associated logging component (FirstScriptExample) has not been enabled. To see your message, you need to enable the logging component FirstScriptExample with a level not lower than NS_LOG_INFO. If you just want to see that particular level of logging, you can turn it on like so,

$ export NS_LOG=FirstScriptExample=info

If you now run the script, you will see a new message "Creating Topology"

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
Creating Topology
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

5.2 Using command line arguments

5.2.1 Overriding default attribute values

Another way to change the behavior of ns-3 scripts without editing and building is by using command line arguments. We provide a mechanism to parse command line arguments and automatically set local and global variables based on the results.

The first step in using the command line argument system is to declare a command line parser. This is quite easy to do (in your main program) as in the following code,

int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}

This simple two line snippet is actually very useful on its own. It opens the door to the ns-3 global variable and attribute system. Let's add two lines of code to the top of the main script function scratch/myfirst.cc. Moving on, we compile the script and run it, at startup we make a help request as follows,

$ ./waf --run "scratch/myfirst --PrintHelp"

This command will ask waf run script scratch/myfirst and pass it a command line argument β€”PrintHelp. The quotes are needed to show which program the argument is for. The command line parser will detect the argument β€”PrintHelp and output the answer

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.

Now consider the option β€”PrintAttributes. We have already mentioned the ns-3 attribute system in the first.cc script. We have seen the following lines of code,

PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

and they said that datarate is actually an attribute PointToPointNetDevice. Let's use the command line argument parser to view the attributes PointToPointNetDevice. The help list says we must provide TypeId. This is the name of the class to which the attributes of interest belong. In our case it will be ns3::PointToPointNetDevice. Let's keep moving forward, enter,

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

The system will print all the attributes of this type of network device. You will see that among the attributes in the list there are,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
The default data rate for point to point links

This is the default value that will be used in the system when the object is created. PointToPointNetDevice. We will override this default value with the parameter Attribute Π² PointToPointHelper higher. Let's use the default values ​​for point-to-point devices and channels. To do this, remove the calls SetDeviceAttribute ΠΈ SetChannelAttribute of myfirst.cc, which we have in a clean directory.

Your script should now simply declare PointToPointHelper and do not perform any installation operations, as shown in the example below,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

Go ahead and create a new script with waf (./waff) and let's go back and enable some recording from the UDP echo server application and include the time prefix.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

If you run the script, you should see the following output:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Recall that the last time we looked at the simulation time, the moment the packet was received by the echo server, it was at 2,00369 seconds.

2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1

Now he receives a packet at 2.25732 seconds. This is because we simply reset the PointToPointNetDevice data rate from five megabits per second to the default value, which is 32768 bits per second. If we were to substitute a new DataRate using the command line, we could speed up our simulation again. We'll do it like this, according to the formula implied by the help element:

$ ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

As a result, the default value of the DataRate attribute will revert to five megabits per second. Are you surprised by the result? It turns out that in order to return the original behavior of the script, we also need to set the channel delay to match the speed of light. We can ask the command line system to print out the channel attributes, just like we did for the network device:

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"

We will find that the channel delay attribute is set as follows:

--ns3::PointToPointChannel::Delay=[0ns]:
Transmission delay through the channel

We can then set both of these defaults through the command line system.

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"

in this case, we are restoring the time we had when we explicitly set the DataRate and Delay in the script:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.00369s Received 1024 bytes from 10.1.1.1
2.00369s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Note that the packet is received again by the server after 2,00369 seconds. We could actually set any of the attributes used in the script this way. In particular, we could set the MaxPackets attributes to values ​​other than one. UdpEchoClient.

How would you use it? Try it. Remember that you should comment out the place where we override the attribute's default value and explicitly set MaxPackets in the script. Then you must rebuild the script. You can also get syntax help on the command line to set a new default attribute value. Once you figure this out, you can control the number of packages displayed on the command line. Since we are diligent people, our command line should look something like this:

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"

The natural question that arises at this point is how to know the existence of all these attributes. Again, the command line system has a help function in this matter. If we request command line help, we should see:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Print the list of globals.
--PrintGroups: Print the list of groups.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintTypeIds: Print all TypeIds.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintHelp: Print this help message.

If you select the "PrintGroups" argument, you should see a list of all registered groups TypeId. The group names match the module names in the source directory (albeit with a capital letter). Printing out all the information at once will be too voluminous, so an additional filter is available to print information by groups. So, again focusing on the point-to-point module:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds in group PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Here you can find the available TypeId names to search for attributes, for example in
--PrintAttributes = ns3 :: PointToPointChannelas shown above.

Another way to learn about attributes is through Doxygen ns-3. There is a page that lists all the attributes registered in the simulator.

5.2.2 Capturing your own commands

You can also add your own hooks via the command line system. This is done quite simply using the command line parser method AddValue.
Let's use this opportunity to specify the number of packages to be displayed in a completely different way. Let's add a local variable named nPackets into a function main. We'll set it to one to match our previous default behavior. To allow the command line parser to change this value, we need to capture this value into the parser. We do this by adding a challenge AddValue. Go and change the script scratch/myfirst.cc so to start with the following code,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
cmd.Parse (argc, argv);
...

Scroll down to the point in the script where we set the MaxPackets attribute and change it to be set to the nPackets variable instead of the constant 1, as shown below.

echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));

Now if you run the script and substitute the --PrintHelp argument, you should see the new user argument. listed on the help display. Enter,

$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.403s)
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.
User Arguments:
--nPackets: Number of packets to echo

If you want to change the number of packets transmitted, you can do so by setting the -nPackets argument on the command line,

$ ./waf --run "scratch/myfirst --nPackets=2"

Now you should now see

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
Sent 1024 bytes to 10.1.1.2
3.25732s Received 1024 bytes from 10.1.1.1
3.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

You have now sent two packages. Pretty simple, right?
You can see that as an ns-3 user, you can use the command line argument system to manipulate global values ​​and attributes. If you are a model author, you can add new attributes to your objects and they will automatically be available for configuration by your users through the command line system. If you are a script writer, you can add new variables to your scripts and painlessly connect them to the command line system.

5.3 Using the trace system

The whole point of simulation is to generate output for further study, and the ns-3 tracing system is the main mechanism for this. Since ns-3 is a C++ program, the standard C++ program output generators can be used:

#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}

You can even use the logging module to add a little structure to your solution. There are many known problems generated by this approach, and therefore, to solve these problems, we have provided a common event tracing subsystem.

The main goals of the ns-3 trace system are:

  • For basic tasks, the tracing system should allow the user to generate standard tracing for popular sources, and select the objects that generate the tracing;

  • Intermediate users should be able to extend the tracing system to change the generated output format, or to insert new trace sources, without modifying the simulator core;

  • Advanced users can modify the simulator core to add new trace sources and sinks. The ns-3 tracing system is built on the principles of independent tracking sources and destinations, as well as a unified mechanism for connecting sources to consumers.

The ns-3 tracing system is built on the principles of independent tracing sources and receivers, as well as a unified mechanism for connecting sources to receivers. Trace sources are objects that can signal events occurring in a simulation and provide access to underlying data of interest. For example, a trace source can indicate when a network device received a packet and make the contents of the packet available to interested trace receivers.

Trace sources on their own are useless unless they are "linked" to other pieces of code that actually do something useful with the information provided by the receiver. Tracers are consumers of events and data provided by trace sources. For example, you can create a trace sink that will (when connected to the trace source of the previous example) print the parts of interest in the received packet.

The rationale for this explicit separation is to allow users to plug in new sink types to existing trace sources without having to edit and recompile the simulator core. Thus, in the example above, the user can only define a new tracer in their script and connect it to an existing trace source defined in the simulation core by editing the user script.

In this tutorial, we'll walk through some of the predefined sources and sinks and show how they can be configured with the least effort on the part of the user. See the ns-3 manual or how-to sections for information on advanced tracing configuration, including extending the tracing namespace and creating new trace sources.

5.3.1 ASCII Tracing

ns-3 provides helper functionality that provides a low-level tracing system to help you with the details when setting up simple packet traces. If you enable this feature, you will see the output in ASCII files. For those familiar with ns-2 output, this type of trace is similar to out.tr, which is generated by many scripts.

Let's get down to business and add some ASCII trace results to our scratch/myfirst.cc script. Right before the call Simulator :: Run (), add the following lines of code:
AsciiTraceHelper ascii;

pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

Like many other ns-3 idioms, this code uses a helper object to generate ASCII traces. The second line contains two nested method calls. Method "inside" CreateFileStream() uses the anonymous object idiom to create a file stream object on the stack (no object name) and pass it to the called method. We will delve into this in the future, but all you need to know at this point is that you are creating an object that represents a file named myfirst.tr and pass it to ns-3. We leave it to ns-3 to take care of the created object for its lifetime, which addresses problems caused by a little-known (intentional) limitation associated with copy constructors of C++ stream objects.

External call EnableAsciiAll() tells the helper that you want to enable tracing in your ASCII simulation for all point-to-point device connections, and that you want the (specified) tracing sinks to record packet movement information in ASCII format.

For those familiar with ns-2, the trace events are equivalent to the known tracepoints that log the "+", "-", "d", and "r" events.
Now you can build the script and run it from the command line:

$ ./waf --run scratch/myfirst

Like so many times before, you will see several messages from Waf and then β€œ'build' finished successfully” with some messages from the running program.

When running, the program will create a file named myfirst.tr. Due to the nature of the work waf, by default the file is not created in the local directory, but in the top-level directory of the repository. If you want to change the path where traces are saved, you can use the Waf parameter to specify it --cwd. We didn't, so in order to look at the ASCII trace file myfirst.tr in your favorite editor, we need to navigate to the top level directory of our repository.

Parsing ASCII traces

There's a lot of information there in a fairly dense form, but the first thing to notice is that the file consists of separate lines. This will be clearly visible if you expand the viewport wider.

Each line in the file corresponds to a trace event. In this case, we are tracing the events in the transmission queue present in each point-to-point network device in the simulation. The transmission queue is the queue through which each packet must pass for a point-to-point link. Note that each line in the trace file starts with a single character (and has a space after it). This character will have the following meaning:

+: enqueuing operation occurred in device queue;
-: an element retrieval operation has occurred in the device queue;
d: the packet was dropped, usually because the queue was full;
r: The packet was received by the network device.

Let's take a closer look at the first line in the trace file. I'll break it into parts (indented for clarity) and the line number on the left:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Point-to-Point Protocol: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
8     length: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      length: 1032 49153 > 9)
11      Payload (size=1024)

The first section of this extended trace event (line 0) is an operation. We have a + symbol here, which corresponds to the operation of queuing for transmission. The second section (line 1) is the simulation time, expressed in seconds. Can you remember what we asked UdpEchoClientApplication start sending packets in two seconds. Here we see confirmation that this is indeed happening.

The next section of the example trace (from line 2) shows which trace source generated this event (namespace trace is specified). You can think of a trace namespace in much the same way as a file system namespace. The namespace root is NodeList. This corresponds to a container managed in ns-3 core code. It contains all the nodes that are created in the script. Just as a file system can have directories at the root, in NodeList we can have many nodes. Thus, the line /NodeList/0 refers to the null node in the NodeList, which we usually understand as "node 0". Each node has a list of devices that have been installed. This list is located next in the namespace. You can see that this trace event is coming from DeviceList/0, which is the device null installed in the node.

next substring, $ ns3 :: PointToPointNetDevice, tells which device is in position zero: the device list of the zero node. Recall that the + operation in line 0 meant that an element was added to the device's transmission queue. This is reflected in the last segments of the "track path": TxQueue/Enqueue.

The rest of the sections in the trace should be fairly intuitive. Lines 3-4 indicate that the packet is encapsulated in a point-to-point protocol. Lines 5-7 show that the packet has an IP4 version header and originated in an IP address 10.1.1.1 and is intended for 10.1.1.2. Lines 8-9 show that this packet has a UDP header and finally line 10 shows that the payload is the expected 1024 bytes.

The next line in the trace file shows that the same packet was retrieved from the transmission queue on the same node.

The third line in the trace file shows that the packet was received by the network device on the node with the echo server. I reproduced this event below.

0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3   ns3::Ipv4Header (
4     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5     length: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       length: 1032 49153 > 9)
8       Payload (size=1024)

Note that the trace operation is now r and the simulation time has been increased to 2,25732 seconds. If you've followed the tutorial's instructions carefully, this means that you've left the Network Device DataRate and Link Delay at their default values. This time should be familiar, as you saw in the previous section.

The trace source namespace entry (line 2) has been changed to reflect that this event originates from node 1 (/NodeList/1) and the packet is accepted by the trace source (/MacRx). It should be pretty easy for you to follow the packet's progress through the topology by looking at the traces left in the file.

5.3.2 PCAP trace

The ns-3 device helpers can also be used to generate .pcap trace files. Acronym pcap (usually written in lowercase) stands for packet capture and is actually an API that includes the definition of the .pcap file format. The most popular program that can read and display this format is Wireshark (previously called Ethereal). However, there are many traffic trace analyzers that use this packet format. We encourage users to use the many tools available to analyze pcap traces. In this tutorial we will focus on viewing pcap traces with Tcpdump.

Enabling pcap tracing is done with one line of code.

pointToPoint.EnablePcapAll ("myfirst");

Paste this line of code after the ASCII trace code we just added to scratch/myfirst.cc. Note that we only passed the string "myfirst" and not "myfirst.pcap" or something like that. This is because the parameter is a prefix, not a fully qualified filename. During the simulation, the assistant will actually create a trace file for each point-to-point device. The filenames will be constructed using the prefix, node number, device number, and the suffix ".pcapΒ».

For our sample script, we will end up with files named "myfirst-0-0.pcap" and "myfirst-1-0.pcap', which are pcap traces for node 0-device 0 and node 1-device 0, respectively. Once you've added a line of code to enable pcap tracing, you can run the script in the usual way:

$ ./waf --run scratch/myfirst

If you look in your distribution's top-level directory, you should see three files: an ASCII trace file myfirst.tr, which we studied before, files myfirst-0-0.pcap ΠΈ myfirst-1-0.pcap are the new pcap files we just generated.

Reading output with tcpdump

For now, the easiest way to view pcap files is to use tcpdump.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
reading from file myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
reading from file myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

In the dump myfirst-0-0.pcap (client device) you can see that the echo packet is sent after 2 seconds of simulation. If you look at the second dump (myfirst-1-0.pcap), you will see that the packet is received at 2,257324 seconds. You will see in the second dump that the packet is returned at 2.257324 seconds, and finally that the packet was received back by the client in the first dump at 2.514648 seconds.

Reading output with Wireshark

If you are not familiar with Wireshark, there is a website from which you can download programs and documentation: http://www.wireshark.org/. Wireshark is a graphical user interface that can be used to display these trace files. If you have Wireshark, you can open any of the trace files and display the contents as if you were capturing packets using a packet sniffer.

Source: habr.com

Add a comment