Skip to main content

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

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>) 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:

Throughput vs Flow ID 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:

NetAnim Animation Screenshot 1 NetAnim Animation Screenshot 2

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.

Wireshark:

Wireshark Trace 1 Wireshark Trace 2 Wireshark Trace 3 Wireshark Trace 4

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.