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/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");
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;
}
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;
}
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);
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();
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");
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
Post a Comment