ns-3 Network Simulator Tutorial. Chapter 5

ns-3 Network Simulator Tutorial. Chapter 5
chapters 1, 2
Chapter 3
Chapter 4

5 Configuration
5.1 Using the Logging Module
5.1.1 Overview of Logging
5.1.2 Enabling 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 Tracing System
5.3.1 ASCII Tracing
Parsing ASCII Traces
5.3.2 PCAP Tracing

Chapter 5

Settings

5.1 Using the Logging Module

We have briefly reviewed the ns-3 logging module while looking at the script first.cc. In this chapter, we will take a closer look at the possible use cases for the logging subsystem.

5.1.1 Overview of Logging

Many large systems support some form of message logging, and ns-3 is no exception. In some cases, only error messages are logged to the 'operator console' (which is usually stderr in Unix-based systems). In other systems, warning messages may also be printed, along with more detailed information. In some cases, logging tools are used to output debug messages that can quickly 'clutter' the output.

The framework used in ns-3 assumes that all these levels of verbosity are useful, and we provide a selective, multi-level approach to message logging. Logging can be fully disabled, enabled for individual components, or at a global level. This is accomplished through customizable verbosity levels. The ns-3 logging module provides a relatively easy way to derive useful information from your simulation.

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

Currently, the system has seven defined levels (types) of log messages in increasing order of verbosity.

  • LOG_ERROR — logging of error messages (associated macro: NS_LOG_ERROR);
  • LOG_WARN — logging warning messages (related macro: NS_LOG_WARN);
  • LOG_DEBUG — logging relatively rare special debug messages (related macro: NS_LOG_DEBUG);
  • LOG_INFO — logging informational messages about the program's execution (related macro: NS_LOG_INFO);
  • LOG_FUNCTION — logging messages describing each called function (two related macros: NS_LOG_FUNCTION for member functions and NS_LOG_FUNCTION_NOARGS for static functions);
  • LOG_LOGIC — logging messages that describe the logical flow within a function (related macro: NS_LOG_LOGIC);
  • LOG_ALL — logging all of the above (there is no related macro).
    For each type (LOG_TYPE), there is also a type LOG_LEVEL_TYPE which, if used, allows logging at its level in addition to all levels above it. (Consequently, LOG_ERROR and LOG_LEVEL_ERROR, as well as LOG_ALL and LOG_LEVEL_ALL are functionally equivalent.) For example, enabling LOG_INFO will allow only messages provided by the macro NS_LOG_INFO, while enabling LOG_LEVEL_INFO will also include messages provided by the macros NS_LOG_DEBUG, NS_LOG_WARN, and NS_LOG_ERROR.

We also provide an unconditional logging macro that displays messages always, regardless of the logging level or selected component.

  • NS_LOG_UNCOND — unconditional logging of the related message (no related logging level).

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

Now that you have carefully read the documentation, let’s use this knowledge to extract some interesting information from the example script scratch/myfirst.cc, which you have already compiled.

5.1.2 Enabling Logging

Let’s use the NS_LOG environment variable to run a few more logs, but first, just to get oriented, 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 and 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 this point, I will assume that you are using a sh-like shell that uses the syntax 'VARIABLE = value'. If you are using a csh-like shell, you will need to convert my examples to the 'setenv variable value' syntax required by those shells.

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

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

It enables the LOG_LEVEL_INFO logging level. When we pass the logging level flag, we are actually enabling that level and all lower levels. In this case, we enabled 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 as follows:

$ export NS_LOG=UdpEchoClientApplication=level_all

Thus, we set the following value for the sh shell NS_LOG variable,

UdpEchoClientApplication=level_all

The left side of the assignment is the name of the loggable component we want to configure, and the right side is the flag we want to apply to it. In this case, we are going to enable all debugging levels for the application. If you run the script with NS_LOG set this way, the ns-3 logging system will take 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()

The additional debug information provided by the application now corresponds to the level NS_LOG_FUNCTION. It shows each instance of function calls during the script execution. Generally, 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 does not require maintaining any logging functionality. The decision on how much information is logged is left entirely to the model developer. For echo applications, there is a wealth of output available for logging.

You can now view the function call logs 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 have expected to see the C++ scope operator (::). This is done intentionally.

It's actually not the class name, but the logging component's name. When there is a match between the source file and class, it is usually the class name, but you need to understand that it is not actually the class name, and there is one colon instead of a double colon. This is a way to conceptually help you separate the logging component's name from the class name.

However, in some cases, it can be challenging to determine which method actually generates the log message. If you look at the text above, you might wonder where the line “Received 1024 bytes from 10.1.1.2” came from. You can solve this problem by setting the level prefix_func in the NS_LOG environment variable. Try doing the following,

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Note that the quotes are necessary because the vertical bar we use to denote the OR operation is also a Unix pipeline connector. Now, if you run the script, you will see that the logging system ensures that each message from this log has a prefix 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()

Now you can see that all messages coming from the UDP echo client application are identified as such. The message "Received 1024 bytes from 10.1.1.2" is now clearly defined as coming from the echo client application. The remaining message should come from the UDP echo server application. We can enable this component by entering a list of components, separated by colons, in the NS_LOG environment variable.

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

Warning: In the text example above, you will need to remove the newline character after the colon (:); it is used for document formatting. Now, if you run the script, you will see all log messages from the client and server echo applications. You may find this very helpful for 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 when a log message was created. You can do this by adding a bit prefix_time:

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

Again, you will need 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 the simulation at 0 seconds. This actually happens before the simulation starts, but this time is displayed as zero seconds. The same is true for the 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 launched the echo server application one second before the simulation began. You can now see that the method StartApplication of the server is actually called at the first second. You can also notice that the echo client starts at the second second of the simulation, as we requested in the script.

Now you can follow the course of the simulation by calling ScheduleTransmit in the client, which calls Send that triggers the HandleRead callback in the echo server application. Note that the elapsed time for sending the packet over the point-to-point link is 3.69 milliseconds. It is evident that the echo server logs a message stating that it has echoed back the packet, and then, after the channel delay, you see that the echo client receives the echo packet in its HandleRead method.

A lot happens unnoticed in this simulation. But you can easily track the entire process by enabling all logging components in the system. Try setting the variable NS_LOG to the following value,

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

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

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

I personally use this extremely verbose logging version when I encounter a problem and have no clue where things went wrong. I can easily track the code execution without setting breakpoints and stepping through code in the debugger. I can simply edit the output in my favorite editor, looking for what I expect and seeing what I didn’t expect to happen. When I have a general idea of what’s going wrong, I switch to the debugger for a deeper investigation of the issue. This kind of output can be particularly useful when your script behaves in completely unexpected ways. If you only use the debugger, you might completely miss an unexpected twist. Logging makes such twists 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 this in the script myfirst.cc, which we have in the 'clean' directory. Remember, we defined the logging component in this script:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

You know that you can enable logging of all messages from this component by setting the NS_LOG environment variable to different levels. Let’s go ahead and add some entries to the script. The macro used to log informational level messages is NS_LOG_INFO. Let’s add a message (just before we start creating nodes) that tells you the script is at the topology creation stage ("Creating Topology"). 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 , so instead of the above command, the following will work:, and clear the NS_LOG variable to disable the logging stream we enabled earlier:

$ ./waf
$ export NS_LOG=
Now, if you run the script,
$ ./waf --run scratch/myfirst

you won’t see the new message, as the related logging component (FirstScriptExample) was not enabled. To see your message, you need to enable the logging component FirstScriptExample at a level of at least NS_LOG_INFO. If you just want to see this specific logging level, you can enable it like this,

$ export NS_LOG=FirstScriptExample=info

If you run the script now, you will see the 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 rebuilding is to use command line arguments. We provide a mechanism for parsing command line arguments and automatically setting local and global variables based on the results.

The first step in using the command line arguments system is to declare the command line parser. This is quite simple (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 in itself. It opens the door to the global ns-3 variable and the attributes system. Let's add two lines of code at the beginning of the script's main function. scratch/myfirst.cc. Moving on, we compile the script and run it, making a help request as follows:

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

This command will ask , described below. Most users will work with to run the script scratch/myfirst and pass it the command line argument --PrintHelp. The quotes are necessary to show which program the argument is meant for. The command line parser will detect the argument --PrintHelp and output a response,

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 let's consider the option --PrintAttributes. We have already mentioned the ns-3 attributes system while examining the script first.cc. We saw the following lines of code,

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

and noted that DataRate is indeed an attribute PointToPointNetDevice. Let's apply the command line argument parser to view the attributes. PointToPointNetDevice. The help list states that 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::PointToPointNetDeviceLet's continue moving forward, enter,

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

The system will print all 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 creating an object PointToPointNetDevice. We will override this default value with the parameter Attribute downward API support (simultaneously with this in PointToPointHelper above. Let's use the default values for point-to-point devices and channels. To do this, we will remove the calls to SetDeviceAttribute and SetChannelAttribute from myfirst.cc, which we have in the clean directory.

Your script should now simply declare PointToPointHelper and not perform any setup 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 , described below. Most users will work with (.\/waf) and let's go back and include some logging from the UDP echo server application and enable 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 last time we looked at the simulation time, the moment the echo server received the packet, it was at 2.00369 seconds.

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

Now it receives the packet at 2.25732 seconds. This is because we simply reset the data transfer rate of PointToPointNetDevice from five megabits per second to the default value of 32768 bits per second. If we set the new DataRate using the command line, we could speed up our simulation again. We'll do this as follows, 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 to restore 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 the channel attributes, just as 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

Then we can set both of these default values 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 by the server again after 2.00369 seconds. We could actually set any of the attributes used in the script this way. In particular, we could set values for MaxPackets other than one. UdpEchoClient.

How would you use this? Give it a try. Remember, you need to comment out where we override the default attribute value and explicitly set it MaxPackets in the script. Then you need to rebuild the script. You can also get command line help on the syntax for setting a new default attribute value. Once you understand this, you will be able to manage the number of packets displayed in the command line. Since we are diligent, our command line should look something like this:

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

A natural question that arises here is how to learn about the existence of all these attributes. Again, the command line system has a help function for this. If we request help from the command line, 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 argument 'PrintGroups', you should see a list of all registered groups. TypeId. Group names correspond to module names in the source directory (with capitalization). Printing all information at once will be too extensive, so an additional filter is available to print information by groups. Thus, focusing again on the 'PointToPoint' module:

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

Here you can find available TypeId names for attribute searches, for example, in
--PrintAttributes = ns3::PointToPointChannel, as shown above.

Another way to learn about attributes is through Doxygen ns-3. There is a page that lists all 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 packets to be displayed in a completely different way. Let's add a local variable named nPackets to the function. mainWe will set it to one to match our previous default behavior. To allow the command-line parser to modify this value, we need to capture it in the parser. We do this by adding a call to AddValue. Go and modify the script scratch/myfirst.cc 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 use the nPackets variable instead of the constant 1, as shown below.

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

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

$ ./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 being sent, you can do so by setting the command line argument -nPackets.

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

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 packets. Quite simple, isn’t it?
You can see that as an ns-3 user, you can use the command-line argument system to control global values and attributes. If you are a model author, you can add new attributes to your objects, and they will be automatically available for configuration by your users through the command-line system. If you are a script author, you can add new variables to your scripts and seamlessly connect them to the command-line system.

5.3 Using the Tracing System

The whole point of modeling is to generate output for further analysis, and the ns-3 tracing system is the primary mechanism for this. Since ns-3 is a C++ program, standard C++ output generation methods can be utilized:

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

You can even use the logging module to add some structure to your solution. Many issues are known to arise from such an approach, and to address these problems we have provided a general event tracing subsystem.

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

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

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

  • Advanced users can modify the simulator core to add new tracing sources and receivers. 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 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. Tracing sources are objects that can signal events occurring in the simulation and provide access to underlying data of interest. For example, a tracing source can indicate when a network device has received a packet and provide access to the packet's content for interested tracing receivers.

Tracing sources are themselves useless if they are not 'connected' to other parts of the code that actually do something useful with the information provided by the receiver. Trace receivers are consumers of events and data provided by tracing sources. For example, one could create a tracing receiver that would (when connected to the tracing source from the previous example) print interesting parts of the received packet.

A rational basis for such an explicit separation is to allow users to connect new types of receivers to existing tracing sources without the need to edit and recompile the simulator core. Thus, in the example above, a user can define a new tracer in their script and connect it to an existing tracing source defined in the simulation core, by only editing the user's script.

In this guide, we will walk through some predefined sources and sinks and show how they can be configured with minimal user effort. See the ns-3 Guide or the instruction sections for information on advanced tracing configuration, including extending the tracing namespace and creating new tracing sources.

5.3.1 ASCII Tracing

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

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

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

As with many other idioms in ns-3, this code uses a helper object to create ASCII traces. The second line contains two nested method calls. The 'inner' method CreateFileStream() uses the anonymous object idiom to create a file stream object on the stack (with no name) and passes it to the calling method. We will delve deeper into this later, but for now, all you need to know is that you are creating an object representing a file named myfirst.tr and passing it to ns-3. We leave it to ns-3 to manage the lifecycle of the created object, during which issues caused by the little-known (intentional) limitation related to C++ stream object copy constructors are resolved.

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

For those familiar with ns-2, the tracked events are equivalent to well-known trace points that log events '+', '-', 'd', and 'r'.
You can now build the script and run it from the command line:

$ ./waf --run scratch/myfirst

How many times before, you will see several messages from Waf, and then 'build' finished successfully with some messages from the running program.

During operation, the program will create a file named myfirst.tr. Due to the specifics of the operation , described below. Most users will work with, by default, the file is created not 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 specify it for Waf using the parameter --cwd. Since we did not do this, to view the ASCII trace file myfirst.tr in your favorite editor, we need to go to the top-level directory of our repository.

Parsing ASCII Traces

There is a lot of information in a fairly dense form, but the first thing to note is that the file consists of separate lines. This will be clearly visible if you widen the viewing window.

Each line in the file corresponds to a trace event. In this case, we are tracing events in the transmission queue present in every point-to-point network device in the simulation. The transmission queue is the queue through which each packet must pass for the point-to-point channel. 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:

+: an enqueue operation occurred in the device queue;
-: a dequeue operation occurred in the device queue;
d: the packet was dropped, usually because the queue is 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 will break it down into parts (with indentation for clarity) and line numbers 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 detailed trace event (line 0) is the operation. Here we have the symbol +, which corresponds to an enqueue operation. The second section (line 1) is the simulation time expressed in seconds. You may recall that we asked UdpEchoClientApplication Start sending packets in two seconds. Here we see confirmation that this is indeed happening.

The next part of the trace example (from line 2) shows which trace source generated this event (the trace namespace is specified). You can think of the trace namespace somewhat like a file system namespace. The root of the namespace is NodeList. This corresponds to the container primarily managed by the ns-3 code. It contains all nodes created in the script. Just as a file system can have directories at its root, we can have multiple nodes in NodeList . Thus, the line /NodeList/0 refers to the zero node in NodeList, which we generally 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 originates from DeviceList/0, which is the zero device installed in the node.

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

The remaining sections of the trace should be intuitive enough. Lines 3-4 indicate that the packet is encapsulated in the point-to-point protocol. Lines 5-7 show that the packet has an IP4 version header and originated from the 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 indicates that the payload is the expected 1024 bytes.

The next line in the trace file shows that the same packet was dequeued 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)

Please note that the trace operation is now r, and the simulation time has increased to 2.25732 seconds. If you followed the tutorial instructions closely, it means you left the DataRate of network devices and channel delay at their default values. This time should be familiar as you have already seen it in the previous section.

The source namespace of the trace (line 2) has been changed to reflect that this event is coming from node 1 (/NodeList/1) и пакет принят источником трассировки (/MacRx). You should find it quite easy to track the packet's movement through the topology by viewing the remaining traces in the file.

5.3.2 PCAP Tracing

The ns-3 device helpers can also be used to create trace files in .pcap format. The acronym pcap (usually written in lowercase) stands for packet capture and is essentially an API that includes the definition of the .pcap file format. The most popular program that can read and display this format is Wireshark (formerly known as Ethereal). However, there are many traffic trace analyzers that utilize this packet format. We recommend users to use the various tools available for analyzing pcap traces. In this guide, we will focus on viewing pcap traces using tcpdump.

Enabling pcap tracing is done with a single line of code.

pointToPoint.EnablePcapAll ("myfirst");

Insert this line of code after the ASCII trace code we just added in scratch/myfirst.cc. Note that we only passed the string 'myfirst' and not 'myfirst.pcap' or anything like that. This is because the parameter is a prefix, not a complete file name. During the simulation, the helper 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 scenario example, we will ultimately see 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 have added the line of code to enable pcap tracing, you can run the script in the usual manner:

$ ./waf --run scratch/myfirst

If you look in the top-level directory of your distribution, you should see three files: the ASCII trace file myfirst.tr, which we examined earlier, and the files myfirst-0-0.pcap and myfirst-1-0.pcap — the new pcap files that we just generated.

Reading output with tcpdump

Currently, 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 (the 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 where you can download programs and documentation: http://www.wireshark.org/. Wireshark — this 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 content as if you had captured the packets using a packet analyzer.

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster