Skip to main content

Implement a simple DNS query-response application over UDP sockets in NS3 | NS3 Project 15

Aim:

To design and simulate a DNS query-response mechanism over UDP sockets using NS3. The client sends QUERY:<domain> packets to a DNS server through a router; the server replies with ANSWER:<domain>:<ip> or NXDOMAIN. The experiment measures throughput, delay, and packet delivery ratio, and visualises packet flow using NetAnim and Gnuplot.

Prompt:

"Implement a simple DNS query-response application over UDP sockets in NS3. The simulation should include a DNS Client node, a Router, and a DNS Server node connected via point-to-point links. The client should send DNS queries (QUERY:<domain>) and the server should respond with IP addresses (ANSWER:<domain>:<ip>) or NXDOMAIN. Include NetAnim animation output, FlowMonitor statistics, and Gnuplot graph generation for throughput and delay.

LLM used: Claude (Anthropic), Gemini

Source Code:

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/netanim-module.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/gnuplot.h"
#include "ns3/mobility-module.h"
#include <string>
#include <map>
#include <vector>
#include <fstream>
#include <iostream>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("DnsSimulation");

/* --- DNS Server Application --- */
class DnsServerApp : public Application {
public:
DnsServerApp() : m_port(53), m_socket(0), m_queryCount(0) {}
static TypeId GetTypeId() {
static TypeId tid = TypeId("DnsServerApp")
.SetParent<Application>()
.SetGroupName("Tutorial")
.AddConstructor<DnsServerApp>();
return tid;
}

void Setup(uint16_t port) {
m_port = port;
m_dnsTable["www.example.com"] = "93.184.216.34";
m_dnsTable["www.google.com"] = "142.250.64.100";
}
uint32_t GetQueryCount() const { return m_queryCount; }
private:
virtual void StartApplication() {
m_socket = Socket::CreateSocket(GetNode(), UdpSocketFactory::GetTypeId());
m_socket->Bind(InetSocketAddress(Ipv4Address::GetAny(), m_port));
m_socket->SetRecvCallback(MakeCallback(&DnsServerApp::HandleRead, this));
}

void HandleRead(Ptr<Socket> socket) {
Ptr<Packet> packet; Address from;
while ((packet = socket->RecvFrom(from))) {
uint8_t buf[256] = {0};
packet->CopyData(buf, sizeof(buf) - 1);
std::string payload((char*)buf);
if (payload.find("QUERY:") == 0) {
m_queryCount++;
std::string domain = payload.substr(6);
std::string response = "ANSWER:" + domain + ":" + (m_dnsTable.count(domain) ? m_dnsTable[domain] : "NXDOMAIN");
Ptr<Packet> resp = Create<Packet>((const uint8_t*)response.c_str(), response.size());
socket->SendTo(resp, 0, from);
}
}
}

uint16_t m_port; Ptr<Socket> m_socket; std::map<std::string, std::string> m_dnsTable; uint32_t m_queryCount;

};



/* --- DNS Client Application --- */

class DnsClientApp : public Application {
public:
DnsClientApp() : m_socket(0), m_queryIndex(0) {}
static TypeId GetTypeId() {
static TypeId tid = TypeId("DnsClientApp")
.SetParent<Application>()
.SetGroupName("Tutorial")
.AddConstructor<DnsClientApp>();
return tid;
}

void Setup(Ipv4Address addr, uint16_t port) {
m_serverAddr = addr;
m_serverPort = port;
m_domains = {"www.example.com", "www.google.com", "www.ns3sim.net", "www.unknown.org", "mail.example.com"};
}
private:
virtual void StartApplication() {
m_socket = Socket::CreateSocket(GetNode(), UdpSocketFactory::GetTypeId());
m_socket->Connect(InetSocketAddress(m_serverAddr, m_serverPort));
m_sendEvent = Simulator::Schedule(Seconds(1.0), &DnsClientApp::SendQuery, this);
}

void SendQuery() {

if (m_queryIndex < m_domains.size()) {

std::string q = "QUERY:" + m_domains[m_queryIndex++];
m_socket->Send(Create<Packet>((const uint8_t*)q.c_str(), q.size()));
m_sendEvent = Simulator::Schedule(Seconds(1.0), &DnsClientApp::SendQuery, this);
}
}
Ipv4Address m_serverAddr; uint16_t m_serverPort; Ptr<Socket> m_socket; EventId m_sendEvent; std::vector<std::string> m_domains; uint32_t m_queryIndex;
};

/* --- Main Simulation --- */
int main(int argc, char* argv[]) {
CommandLine cmd;
cmd.Parse(argc, argv);
NodeContainer nodes;
nodes.Create(3);

// Mobility
MobilityHelper mobility;
Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
positionAlloc->Add(Vector(10.0, 50.0, 0.0));
positionAlloc->Add(Vector(50.0, 50.0, 0.0));
positionAlloc->Add(Vector(90.0, 50.0, 0.0));
mobility.SetPositionAllocator(positionAlloc);
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(nodes);
PointToPointHelper p2p;
p2p.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
p2p.SetChannelAttribute("Delay", StringValue("2ms"));
NetDeviceContainer d01 = p2p.Install(nodes.Get(0), nodes.Get(1));
NetDeviceContainer d12 = p2p.Install(nodes.Get(1), nodes.Get(2));
InternetStackHelper stack;
stack.Install(nodes);
Ipv4AddressHelper address;
address.SetBase("10.1.1.0", "255.255.255.0");
address.Assign(d01);
address.SetBase("10.1.2.0", "255.255.255.0");
Ipv4InterfaceContainer i12 = address.Assign(d12);
Ipv4GlobalRoutingHelper::PopulateRoutingTables();

// NetAnim
AnimationInterface anim("dns-anim.xml");
anim.UpdateNodeDescription(nodes.Get(0), "Client");
anim.UpdateNodeDescription(nodes.Get(1), "Router");
anim.UpdateNodeDescription(nodes.Get(2), "Server");
p2p.EnablePcapAll("dns-trace");

// Applications
Ptr<DnsServerApp> server = CreateObject<DnsServerApp>();
server->Setup(53);
nodes.Get(2)->AddApplication(server);
server->SetStartTime(Seconds(1.0));
Ptr<DnsClientApp> client = CreateObject<DnsClientApp>();
client->Setup(i12.GetAddress(1), 53);
nodes.Get(0)->AddApplication(client);
client->SetStartTime(Seconds(2.0));
FlowMonitorHelper flowmon;
Ptr<FlowMonitor> monitor = flowmon.InstallAll();
Simulator::Stop(Seconds(15.0));
Simulator::Run();

// Gnuplot
Gnuplot plot("throughput.png");
plot.SetTitle("Throughput vs Flow ID");
plot.SetTerminal("png");
Gnuplot2dDataset dataset;
dataset.SetStyle(Gnuplot2dDataset::LINES_POINTS);
monitor->CheckForLostPackets();
Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier>(flowmon.GetClassifier());
std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats();
std::cout << "\n--- Flow Statistics ---" << std::endl;
for (auto it = stats.begin(); it != stats.end(); ++it) {
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(it->first);
double duration = it->second.timeLastRxPacket.GetSeconds() - it->second.timeFirstTxPacket.GetSeconds();
double throughput = (duration > 0) ? (it->second.rxBytes * 8.0 / (duration * 1000.0)) : 0;
std::cout << "Flow " << it->first << " (" << t.sourceAddress << " -> " << t.destinationAddress << "): "
<< throughput << " kbps [Rx Packets: " << it->second.rxPackets << "]" << std::endl;
dataset.Add((double)it->first, throughput);
}
plot.AddDataset(dataset);
std::ofstream plotFile("dns-throughput.plt");
plot.GenerateOutput(plotFile);
plotFile.close();
Simulator::Destroy();
return 0;
}

Graph:

The graph plots throughput (in kbps) on the Y-axis against Flow ID on the X-axis. Two flows are recorded — Flow 1 (client → server, DNS query direction) and Flow 2 (server → client, DNS response direction).

  • Flow 1 throughput: ~0.484 kbps
  • Flow 2 throughput: ~0.608 kbps
  • The relationship is linear and increasing from Flow 1 to Flow 2.

 

NetAnim:

Linear 3-node chain - Client connected to Router via P2P, Router to DNS Server via P2P. Animated arrow show DNS query packets traversing the network and response packets returning. All routing via Ipv4GlobalRoutingHelper


WireShark:

 

 

Comments

Popular posts from this blog

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide]

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide] Are you planning to install or try Ubuntu 24.04 LTS ? The easiest and most reliable method is to create a bootable USB drive using Rufus on a Windows system. This detailed guide will help you create a Ubuntu 24.04 USB bootloader using Rufus with easy-to-follow steps and screenshots (optional). Here is the complete video of the bootloader creation and OS installation in Windows 11. 🧰 Requirements A USB flash drive (minimum 8GB recommended) A Windows PC Ubuntu 24.04 LTS ISO file Rufus USB creation tool 🧾 Steps to Create a Ubuntu 24.04 Bootable USB Using Rufus ✅ Step 1: Download Ubuntu 24.04 ISO Visit the official Ubuntu website and download the Ubuntu 24.04 LTS ISO file . ✅ Step 2: Download and Run Rufus Head to Rufus official site and download the latest version. Open the executable file (no installation required). ✅ Step 3: Insert USB Drive Plug in your USB drive. Rufus ...

Installing ns3 in Ubuntu 22.04 | Complete Instructions

In this post, we are going to see how to install ns-3.36.1 in Ubuntu 22.04. You can follow the video for complete details Tools used in this simulation: NS3 version ns-3.36.1  OS Used: Ubuntu 22.04 LTS Installation of NS3 (ns-3.36.1) There are some changes in the ns3 installation procedure and the dependencies. So open a terminal and issue the following commands Step 1:  Prerequisites $ sudo apt update In the following packages, all the required dependencies are taken care and you can install all these packages for the complete use of ns3. $ sudo apt install g++ python3 python3-dev pkg-config sqlite3 cmake python3-setuptools git qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3 openmpi-bin openmpi-common openmpi-doc libopenmpi-dev autoconf cvs bzr unrar gsl-bin libgsl-dev libgslcblas0 wireshark tcpdump sqlite sqlite3 libsqlite3-dev  libxml2 libxml2-dev libc6-dev libc6-dev-i386 libc...

NS2 (NS-2.35) Installation in Ubuntu 11.10

This post will help you in installing Network Simulator 2 version NS2.35 in Ubuntu 11.10 Instructions Install Ubuntu Download NS-2.35 ( http://sourceforge.net/projects/nsnam/files/allinone/ns-allinone-2.35/ns-allinone-2.35.tar.gz/download ) Unzip or untar it to any folder (recommended is /home/ loginname) using the following commands one by one sudo apt-get update sudo apt-get install build-essential autoconf automake libxmu-dev tar zxvf ns-allinone-2.35.tar.gz cd ns-allinone-2.35 ./install Once installed the PATH information will be provided to you. Copy the PATH and LD_LIBRARY_PATH Variable to .bashrc (see a dot in the beginning) Input the path information in .bashrc file like this export PATH=$PATH:<Place your paths here> export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: <place the LD_LIBRARY_PATHS> here. Once done, save the file and close execute the command source .bashrc try ns or nam to see whether your installation succeeded.