Simulation and Analysis of DNS Query-Response Mechanism over UDP in NS-3
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>) orNXDOMAIN. 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 arrows show DNS query packets traversing the network and response packets returning. All routing via Ipv4GlobalRoutingHelper.
Comments
Post a Comment