ns-3 learning notes 2

ns-3 is a powerful network simulator. This is a note for learning how ns-3 works.

official tutorial at here.

changing queue disciplines

there are 2 types of queues in ns3

  • qos queue before ip layer:
    • PFifoFastQueueDisc: The default maximum size is 1000 packets //default
    • FifoQueueDisc: The default maximum size is 1000 packets
    • RedQueueDisc: The default maximum size is 25 packets
    • CoDelQueueDisc: The default maximum size is 1500 kilobytes
    • FqCoDelQueueDisc: The default maximum size is 10024 packets
    • PieQueueDisc: The default maximum size is 25 packets
    • MqQueueDisc: This queue disc has no limits on its capacity
    • TbfQueueDisc: The default maximum size is 1000 packets
  • device queue according to phy link:
    • PointToPointNetDevice: The default configuration (as set by the helper) is to install a DropTail queue of default size (100 packets)
    • CsmaNetDevice: The default configuration (as set by the helper) is to install a DropTail queue of default size (100 packets)
    • WiFiNetDevice: The default configuration is to install a DropTail queue of default size (100 packets) for non-QoS stations and four DropTail queues of default size (100 packets) for QoS stations
    • SimpleNetDevice: The default configuration is to install a DropTail queue of default size (100 packets)
    • LTENetDevice: Queueing occurs at the RLC layer (RLC UM default buffer is 10 * 1024 bytes, RLC AM does not have a buffer limit).
    • UanNetDevice: There is a default 10 packet queue at the MAC layer

We can modify them like:

1
2
PointToPointHelper p2p;
p2p.SetQueue ("ns3::DropTailQueue", "MaxSize", StringValue ("50p"));
1
2
3
TrafficControlHelper tch;
tch.SetRootQueueDisc ("ns3::CoDelQueueDisc", "MaxSize", StringValue ("1000p"));
tch.Install (devices);

BQL can be enabled on a device that supports it through the traffic control helper:

1
2
3
4
TrafficControlHelper tch;
tch.SetRootQueueDisc ("ns3::CoDelQueueDisc", "MaxSize", StringValue ("1000p"));
tch.SetQueueLimits ("ns3::DynamicQueueLimits", "HoldTime", StringValue ("4ms"));
tch.Install (devices);

advanced tracing system

Tracing source is used to collect data, and tracing sink is used to deal with the data.

tracing source

tracing source is predefined associated with the target variable. When the target variable changes, system will call the callback function we connect.

for example, in the following script we define a tracing source. the kernel code is addtracesource. the first argument is the source name. the second is a help string. the third one make a class member be the traced value. the last one is a typedef for the traced value type, which we will use to generate the correct callback function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyObject : public Object
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent (Object::GetTypeId ())
.SetGroupName ("MyGroup")
.AddConstructor<MyObject> ()
.AddTraceSource ("MyInteger",
"An integer value to trace.",
MakeTraceSourceAccessor (&MyObject::m_myInt),
"ns3::TracedValueCallback::Int32")
;
return tid;
}

MyObject () {}
TracedValue<int32_t> m_myInt;
};

tracing sink

tracing sink is a callback function to do what we want. the parameter should be related to the type of the traced value.

1
2
3
4
5
void
IntTrace (int32_t oldValue, int32_t newValue)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}

connect the source and sink

normally we use an object’s method the connect the source and sink.

1
2
3
4
5
6
7
8
int
main (int argc, char *argv[])
{
Ptr<MyObject> myObject = CreateObject<MyObject> ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));

myObject->m_myInt = 1234;
}

then if the traced value changes, ns3 will call the callback function already registered.

Note that we need to first create the object, and then hook it. I.e. first create the tcpsocket, then trace the cwnd.

additional sink function parameter

we can add parameter to the sink function

1
2
3
4
5
6
7
8
9
10
11
12
13
static void
CwndChange (Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
{
NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd);
*stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl;
}

static void
RxDrop (Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
{
NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ());
file->Write(Simulator::Now(), p);
}
1
2
3
4
5
6
7
AsciiTraceHelper asciiTraceHelper;
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream ("sixth.cwnd");
ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream));

PcapHelper pcapHelper;
Ptr<PcapFileWrapper> file = pcapHelper.CreateFile ("sixth.pcap", std::ios::out, PcapHelper::DLT_PPP);
devices.Get (1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback (&RxDrop, file));

create an application

we can use the class application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyApp : public Application
{
public:

MyApp ();
virtual ~MyApp();

void Setup (Ptr<Socket> socket, Address address, uint32_t packetSize,
uint32_t nPackets, DataRate dataRate);

private:
virtual void StartApplication (void);
virtual void StopApplication (void);
...
};

we need to implement how to start and stop application. Then in the main function we can set start and stop time. the node list will automatically call the startapplication and stopapplication.

1
2
3
4
5
Ptr<MyApp> app = CreateObject<MyApp> ();
app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps"));
nodes.Get (0)->AddApplication (app);
app->Start (Seconds (1.));
app->Stop (Seconds (20.));

a receive-no-reply server application

1
2
3
4
5
6
7
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 (20.));

trace helper

we can use trace helper to trace either net device or protocol, in ascii and pcap ways.

i.e. for net device object, there are several overrides:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void EnablePcap (std::string prefix, Ptr<NetDevice> nd, bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::string prefix, NetDeviceContainer d, bool promiscuous = false);
void EnablePcap (std::string prefix, NodeContainer n, bool promiscuous = false);
void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool promiscuous = false);
void EnablePcapAll (std::string prefix, bool promiscuous = false);

void EnableAscii (std::string prefix, Ptr<NetDevice> nd, bool explicitFilename = false);
void EnableAscii (Ptr<OutputStreamWrapper> stream, Ptr<NetDevice> nd);

Names::Add ("client" ...);
Names::Add ("client/eth0" ...);
Names::Add ("server" ...);
Names::Add ("server/eth0" ...);
void EnableAscii (std::string prefix, std::string ndName, bool explicitFilename = false);
void EnableAscii (Ptr<OutputStreamWrapper> stream, std::string ndName);

void EnableAscii (std::string prefix, NetDeviceContainer d);
void EnableAscii (Ptr<OutputStreamWrapper> stream, NetDeviceContainer d);

void EnableAscii (std::string prefix, NodeContainer n);
void EnableAscii (Ptr<OutputStreamWrapper> stream, NodeContainer n);

void EnableAsciiAll (std::string prefix);
void EnableAsciiAll (Ptr<OutputStreamWrapper> stream);

void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
void EnableAscii (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t deviceid);

note if we use implicit filename, the prefix would be the filename. default filename would be --.pcap/tr.

similar with protocol. default filename would be -n-i.pcap/tr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void EnablePcapIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std::string prefix, NodeContainer n);
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface, bool explicitFilename);
void EnablePcapIpv4All (std::string prefix);
void EnableAsciiIpv4 (std::string prefix, Ptr<Ipv4> ipv4, uint32_t interface, bool explicitFilename = false);
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ptr<Ipv4> ipv4, uint32_t interface);

void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface, bool explicitFilename = false);
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, std::string ipv4Name, uint32_t interface);

void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, Ipv4InterfaceContainer c);

void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, NodeContainer n);

void EnableAsciiIpv4All (std::string prefix);
void EnableAsciiIpv4All (Ptr<OutputStreamWrapper> stream);

void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid, bool explicitFilename);
void EnableAsciiIpv4 (Ptr<OutputStreamWrapper> stream, uint32_t nodeid, uint32_t interface);