We now move into part II of the ns-3-click tutorial series wherein I'll walk you through an ns-3 script and explain how to load your Click scripts onto ns-3 nodes. This tutorial goes through the following steps:

  1. Walkthrough of an example Click graph from ns-3-click.
  2. Using the Click graph in an ns-3 simulation.

 

Walkthrough of an example Click graph from ns-3-click

The Click script I'll be explaining below is available in current ns-3-dev and stable releases from ns-3.11 onwards. You can find it in src/click/examples/nsclick-lan-single-interface.click.

When working with ns-3-click, you will need to handle all layer 3 functionalities expected of a networked node from Click. The Click script that we'll be talking about here provides exactly this. It handles ARP, and forwards packets up and down the stack as required. It has a single network interface (eth0) to send and receive packets, and has a kernel interface (tap0) to send/receive packets from the kernel (in ns-3, this corresponds to communicating with layer 4).

We first describe the interface to tap0 with the following functionalities:

  1. All packets received from tap0 should be forwarded down to the stack.
  2. All packets received from below the stack, destined to us, should be sent up the stack via tap0.

[sourcecode]

elementclass TapSimHost {

$dev |

// Packets go to tap0, which sends them to the kernel

input[0]

-> ToDump(tokernel.pcap,2000,IP,PER_NODE 1)

-> ToSimDevice($dev,IP);

// Packets sent out by the kernel get pushed outside

FromSimDevice($dev,4096)

-> CheckIPHeader2

-> ToDump(fromkernel.pcap,2000,IP,PER_NODE 1)

-> GetIPAddress(16)

-> [0]output;

}

kernel::TapSimHost(tap0);[/sourcecode]

 

The above snippet does exactly what we've described so far. We create an element class which has a single input and output. Packets received on the input are plumbed to tap0 (because $dev is now tap0, as per the instantiation in the last line). Packets received from tap0 are pushed outside. We now describe a LAN host, which will handle ARP, and check the destination IP to see if we should receive the packet.

[sourcecode]

elementclass LanSimHost {

$ipaddr, $hwaddr |

cl::Classifier(12/0806 20/0001,12/0806 20/0002, -);

forhost::IPClassifier(dst host $ipaddr,-);

arpquerier::ARPQuerier(eth0);

arpresponder::ARPResponder(eth0);

ethout::Queue

-> ToDump(out_eth0.pcap,PER_NODE 1)

-> ToSimDevice(eth0);

// All packets received on eth0 are silently

// dropped if they are destined for another location

FromSimDevice(eth0,4096)

-> ToDump(in_eth0.pcap,PER_NODE 1,ENCAP ETHER)

-> cl;

// ARP queries from other nodes go to the ARP responder element

cl[0] -> arpresponder;

// ARP responses go to our ARP query element

cl[1] -> [1]arpquerier;

// All other packets get checked whether they are meant for us

cl[2]

-> Strip(14)

-> CheckIPHeader2

-> MarkIPHeader

-> GetIPAddress(16) // Sets destination IP address annotation

-> forhost;

// Packets for us are pushed outside

forhost[0]

-> [0]output;

// Packets for other folks or broadcast

// packets get sent to output 1

forhost[1]

-> ToDump(discard.pcap,2000,PER_NODE 1,ENCAP IP)

-> [1]output;

// Incoming packets get pushed into the ARP query module

input[0]

-> arpquerier;

// Both the ARP query and response modules send data out to

// the simulated network device, eth0.

arpquerier

-> ToDump(out_arpquery.pcap,PER_NODE 1)

-> ethout;

arpresponder

-> ToDump(out_arprespond.pcap,PER_NODE 1)

-> ethout;

}

lan::LanSimHost(eth0:ip,eth0:eth);

[/sourcecode]

 

Now that we have a LanSimHost type ready, and instantiated (in the last line indicated above), we perform the final plumbing required to connect our LanSimHost to our kernel tap device:

[sourcecode]

// Users can do some processing between the two elements

lan[0] -> kernel;

kernel -> lan;

// Packets for others or broadcasts are discarded

lan[1] -> Discard;

[/sourcecode]

This concludes our description of the nsclick-lan-single-interface.click file. Let's now describe an ns-3 simulation script for our scenario.

 

Using the Click graph in an ns-3 script

I'll now describe a simple ns-3 script which makes use of the above described Click graph. Not surprisingly, the script is named nsclick-simple-lan.cc and can be found within src/click/examples/. The simulation scenario is a simple one: two nodes A and B connected via a CSMA channel, with A sending B a stream of packets using a TCP connection. A is Click based, whereas B is a normal ns-3 node.

The first step would be to create the nodes.

[sourcecode language="cpp"]

NodeContainer csmaNodes;

csmaNodes.Create (2);

[/sourcecode]

Next, we create a CSMA channel.

[sourcecode language="cpp"]

CsmaHelper csma;

csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate (5000000)));

csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));

NetDeviceContainer csmaDevices = csma.Install (csmaNodes);

[/sourcecode]

We then install a normal internet stack on node B.

[sourcecode language="cpp"]

InternetStackHelper internet;

internet.Install (csmaNodes.Get (1));

[/sourcecode]

And then setup a Click based internet stack on node A. We need to specify the Click script that the particular node is supposed to use, and in the event that we require a Click based node to run a traffic generator on top, we need to specify a routing table element for the node to use. This can be seen by the name "rt" at the end of the nsclick-lan-single-interface.click file.

[sourcecode language="cpp"]

ClickInternetStackHelper clickinternet;

clickinternet.SetClickFile (csmaNodes.Get (0), "src/click/examples/nsclick-lan-single-interface.click");

clickinternet.SetRoutingTableElement (csmaNodes.Get (0), "rt");

clickinternet.Install (csmaNodes.Get (0));

[/sourcecode]

Now that all the nodes have an internet stack, we assign IPv4 addresses to all the network interfaces.

[sourcecode language="cpp"]

Ipv4AddressHelper ipv4;

ipv4.SetBase ("172.16.1.0", "255.255.255.0");

ipv4.Assign (csmaDevices);

[/sourcecode]

And then, we setup the traffic generators for talking between node A and node B.

[sourcecode language="cpp"]

Address LocalAddress (InetSocketAddress (Ipv4Address::GetAny (), 50000));

PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", LocalAddress);

ApplicationContainer recvapp = packetSinkHelper.Install (csmaNodes.Get (1));

recvapp.Start (Seconds (5.0));

recvapp.Stop (Seconds (10.0));

OnOffHelper onOffHelper ("ns3::TcpSocketFactory", Address ());

onOffHelper.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (1)));

onOffHelper.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));

ApplicationContainer appcont;

addressValue remoteAddress (InetSocketAddress (Ipv4Address ("172.16.1.2"), 50000));

onOffHelper.SetAttribute ("Remote", remoteAddress);

appcont.Add (onOffHelper.Install (csmaNodes.Get (0)));

appcont.Start (Seconds (5.0));

appcont.Stop (Seconds (10.0));

[/sourcecode]

Lastly, we enable PCAP tracing on all the CSMA NetDevices in the scenario.

[sourcecode language="cpp"]

csma.EnablePcap ("nsclick-simple-lan", csmaDevices, false);

[/sourcecode]

And to conclude the script, we specify the running time for the simulation to be 20 seconds, and call Simulator::Run(). Don't forget to call Simulator::Destroy() lest tools like Valgrind start screaming about memory leaks.

[sourcecode language="cpp"]

Simulator::Stop (Seconds (20.0));

Simulator::Run ();

Simulator::Destroy ();

return 0;

[/sourcecode]

 

To see the results of running our simulation, execute the below in your terminal from the ns-3-dev top level directory once you've built Click as described in the previous article.

$: ./waf --run nsclick-simple-lan

Have a look at the resulting pcap traces (nsclick-simple-lan-0-[0,1].pcap) using wireshark or tcpdump to see what happened through the simulation.

Hope you found this little walkthrough helpful. If you find any bugs with ns-3-click, please don't hesitate to file a bug report on our bugzilla. :)