Skip to content

Getting Started with ns-3: A Guide to Network Simulation and 5G Modeling

Note

Author: Pei-Chi, Cheng
Date: 2025/10/08


1. Introduction

ns-3 is a discrete-event network simulator designed primarily for academic research and education. Unlike network emulation tools that mimic real-world networks, ns-3 focuses on simulating network behavior in a controlled, virtual environment. It is widely used to study network protocols, evaluate performance, and test network designs.

What is Discrete-Event Simulation?

Discrete-event simulation processes events (e.g., packet transmission, node movement) at specific time points, making it efficient for modeling complex network scenarios without continuous computation.

Key Features

  • Modular Design: ns-3 is highly modular, allowing users to customize and extend its functionality.
  • Protocol Support: Supports a wide range of protocols including IPv4/IPv6, TCP/UDP, LTE/5G, Wi-Fi, and more.
  • Event-Driven: Operates as a discrete-event simulator, processing events in a time-based sequence.
  • Programming APIs: Provides APIs for both C++ and Python, catering to different user preferences.

Use Cases

  • Researching and developing network protocols.
  • Designing and testing network topologies.
  • Simulating wireless, satellite, or mobile networks.
  • Educational demonstrations for networking concepts.

2. Installation and Environment Setup

System Requirements

ns-3 is primarily developed for Linux environments, with Ubuntu being the most commonly used platform. Required tools include:

  • g++: C++ compiler.
  • Python: For scripting and PyBind support.
  • CMake: For build configuration.
  • Additional dependencies like libxml2, sqlite, and others may be needed depending on modules.

Installation Steps

Step 1: Install Dependencies:

sudo apt update
sudo apt install -y git g++ python3 python3-dev \
  cmake ninja-build pkg-config sqlite3 \
  libsqlite3-dev libboost-all-dev \
  libgsl-dev libgtk-3-dev

Step 2: Download ns-3 Source Code (using version 3.42 as an example)

wget https://www.nsnam.org/releases/ns-allinone-3.42.tar.bz2
tar xfj ns-allinone-3.42.tar.bz2
cd ns-allinone-3.42/ns-3.42

Step 3: Configure and Build:

./ns3 configure --enable-examples --enable-tests
./ns3 build

Upon successful compilation, the terminal will display the configured modules, indicating which packages are included in your ns-3 simulator.
image

Step 4: Verify Installation:

Run a sample program to confirm the setup:

./ns3 run hello-simulator

A successful run will produce output confirming the simulator is operational.
image

macOS Installation Notes

For macOS, install dependencies using Homebrew:

brew install git python3 cmake ninja pkg-config sqlite boost gsl gtk+3

For Windows, use WSL2 with Ubuntu or a Docker container.

3. Understanding ns-3 Directory Structure

To navigate ns-3 effectively, understanding its directory structure is crucial. Below are key folders and their purposes:

Directory Description
scratch/ User-written test scripts for quick experiments
examples/ Official example programs demonstrating built-in features
src/ Core protocol modules (e.g., Wi-Fi, LTE, TCP/IP stack)
utils/ Utility tools for trace analysis and data collection
contrib/ Community-contributed extensions (e.g., 5G LENA)
build/ Compiled binaries and output files

4. Compiling and Running Simulations

Running a Scratch Program

To execute a user-written script (e.g., mytest) in the scratch/ directory:

./ns3 run scratch/mytest

Note: In older ns-3 versions, use ./waf instead of ./ns3:
./waf run scratch/mytest

Running an Example

To execute an official example:

./ns3 run examples/tutorial/first.cc

Adding Parameters

Pass custom parameters to simulations:

./ns3 run "scratch/mytest --param=5 --duration=10s"

5. Example: The 3GPP Channel Model Simulation

Overview

The cttc-3gpp-channel-example.cc from the 5G LENA module simulates a 5G NR network with:

  • RemoteHost: Sends UDP traffic to user equipment (UEs).
  • PGW: Connects UEs to the external network.
  • gNB1, gNB2: 5G base stations.
  • UE1, UE2: User equipment nodes.

The simulation configures:

  • 3GPP TR 38.901 channel model.
  • Antennas, beamforming, and TDMA-RR scheduler.
  • UDP downlink traffic (10 packets per UE).

The TR 38.901 model provides realistic channel characteristics for 5G, supporting scenarios like:

  • UMa (Urban Macro): High base stations in urban areas.
  • RMa (Rural Macro): Wide coverage in rural settings.
  • UMi-StreetCanyon: Urban street-level environments.

Code Example

Below is the core code for cttc-3gpp-channel-example.cc. It leverages ns-3 Helper classes to simplify the setup of complex network components.

// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
//
// SPDX-License-Identifier: GPL-2.0-only

/**
 * \file cttc-3gpp-channel-example.cc
 * \ingroup examples
 * \brief Channel Example
 *
 * This example describes how to setup a simulation using the 3GPP channel model
 * from TR 38.901. Topology consists by default of 2 UEs and 2 gNbs, and can be
 * configured to be either mobile or static scenario.
 *
 * The output of this example are default NR trace files that can be found in
 * the root ns-3 project folder.
 */

#include "ns3/applications-module.h"
#include "ns3/config-store.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/log.h"
#include "ns3/mobility-module.h"
#include "ns3/nr-helper.h"
#include "ns3/nr-mac-scheduler-tdma-rr.h"
#include "ns3/nr-module.h"
#include "ns3/nr-point-to-point-epc-helper.h"
#include "ns3/point-to-point-helper.h"
#include <ns3/antenna-module.h>
#include <ns3/buildings-helper.h>
#include "ns3/netanim-module.h"

using namespace ns3;

int
main(int argc, char* argv[])
{
    std::string scenario = "UMa"; // scenario
    double frequency = 28e9;      // central frequency
    double bandwidth = 100e6;     // bandwidth
    double mobility = false;      // whether to enable mobility
    double simTime = 1;           // in second
    double speed = 1;             // in m/s for walking UT.
    bool logging = true; // whether to enable logging from the simulation, another option is by
                         // exporting the NS_LOG environment variable
    double hBS;          // base station antenna height in meters
    double hUT;          // user antenna height in meters
    double txPower = 40; // txPower
    enum BandwidthPartInfo::Scenario scenarioEnum = BandwidthPartInfo::UMa;

    CommandLine cmd(__FILE__);
    cmd.AddValue("scenario",
                 "The scenario for the simulation. Choose among 'RMa', 'UMa', 'UMi-StreetCanyon', "
                 "'InH-OfficeMixed', 'InH-OfficeOpen'.",
                 scenario);
    cmd.AddValue("frequency", "The central carrier frequency in Hz.", frequency);
    cmd.AddValue("mobility",
                 "If set to 1 UEs will be mobile, when set to 0 UE will be static. By default, "
                 "they are mobile.",
                 mobility);
    cmd.AddValue("logging", "If set to 0, log components will be disabled.", logging);
    cmd.Parse(argc, argv);

    // enable logging
    if (logging)
    {
        // LogComponentEnable ("ThreeGppSpectrumPropagationLossModel", LOG_LEVEL_ALL);
        LogComponentEnable("ThreeGppPropagationLossModel", LOG_LEVEL_ALL);
        // LogComponentEnable ("ThreeGppChannelModel", LOG_LEVEL_ALL);
        // LogComponentEnable ("ChannelConditionModel", LOG_LEVEL_ALL);
        // LogComponentEnable ("UdpClient", LOG_LEVEL_INFO);
        // LogComponentEnable ("UdpServer", LOG_LEVEL_INFO);
        // LogComponentEnable ("NrRlcUm", LOG_LEVEL_LOGIC);
        // LogComponentEnable ("NrPdcp", LOG_LEVEL_INFO);
    }

    /*
     * Default values for the simulation. We are progressively removing all
     * the instances of SetDefault, but we need it for legacy code (LTE)
     */
    Config::SetDefault("ns3::NrRlcUm::MaxTxBufferSize", UintegerValue(999999999));

    // set mobile device and base station antenna heights in meters, according to the chosen
    // scenario
    if (scenario == "RMa")
    {
        hBS = 35;
        hUT = 1.5;
        scenarioEnum = BandwidthPartInfo::RMa;
    }
    else if (scenario == "UMa")
    {
        hBS = 25;
        hUT = 1.5;
        scenarioEnum = BandwidthPartInfo::UMa;
    }
    else if (scenario == "UMi-StreetCanyon")
    {
        hBS = 10;
        hUT = 1.5;
        scenarioEnum = BandwidthPartInfo::UMi_StreetCanyon;
    }
    else if (scenario == "InH-OfficeMixed")
    {
        hBS = 3;
        hUT = 1;
        scenarioEnum = BandwidthPartInfo::InH_OfficeMixed;
    }
    else if (scenario == "InH-OfficeOpen")
    {
        hBS = 3;
        hUT = 1;
        scenarioEnum = BandwidthPartInfo::InH_OfficeOpen;
    }
    else
    {
        NS_ABORT_MSG("Scenario not supported. Choose among 'RMa', 'UMa', 'UMi-StreetCanyon', "
                     "'InH-OfficeMixed', and 'InH-OfficeOpen'.");
    }

    // create base stations and mobile terminals
    NodeContainer gnbNodes;
    NodeContainer ueNodes;
    gnbNodes.Create(2);
    ueNodes.Create(2);

    // position the base stations
    Ptr<ListPositionAllocator> gnbPositionAlloc = CreateObject<ListPositionAllocator>();
    gnbPositionAlloc->Add(Vector(0.0, 0.0, hBS));
    gnbPositionAlloc->Add(Vector(0.0, 80.0, hBS));
    MobilityHelper gnbMobility;
    gnbMobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
    gnbMobility.SetPositionAllocator(gnbPositionAlloc);
    gnbMobility.Install(gnbNodes);

    // position the mobile terminals and enable the mobility
    MobilityHelper uemobility;
    uemobility.SetMobilityModel("ns3::ConstantVelocityMobilityModel");
    uemobility.Install(ueNodes);

    if (mobility)
    {
        ueNodes.Get(0)->GetObject<MobilityModel>()->SetPosition(
            Vector(90, 15, hUT)); // (x, y, z) in m
        ueNodes.Get(0)->GetObject<ConstantVelocityMobilityModel>()->SetVelocity(
            Vector(0, speed, 0)); // move UE1 along the y axis

        ueNodes.Get(1)->GetObject<MobilityModel>()->SetPosition(
            Vector(30, 50.0, hUT)); // (x, y, z) in m
        ueNodes.Get(1)->GetObject<ConstantVelocityMobilityModel>()->SetVelocity(
            Vector(-speed, 0, 0)); // move UE2 along the x axis
    }
    else
    {
        ueNodes.Get(0)->GetObject<MobilityModel>()->SetPosition(Vector(90, 15, hUT));
        ueNodes.Get(0)->GetObject<ConstantVelocityMobilityModel>()->SetVelocity(Vector(0, 0, 0));

        ueNodes.Get(1)->GetObject<MobilityModel>()->SetPosition(Vector(30, 50.0, hUT));
        ueNodes.Get(1)->GetObject<ConstantVelocityMobilityModel>()->SetVelocity(Vector(0, 0, 0));
    }

    /*
     * Create NR simulation helpers
     */
    Ptr<NrPointToPointEpcHelper> nrEpcHelper = CreateObject<NrPointToPointEpcHelper>();
    Ptr<IdealBeamformingHelper> idealBeamformingHelper = CreateObject<IdealBeamformingHelper>();
    Ptr<NrHelper> nrHelper = CreateObject<NrHelper>();
    nrHelper->SetBeamformingHelper(idealBeamformingHelper);
    nrHelper->SetEpcHelper(nrEpcHelper);

    /*
     * Spectrum configuration. We create a single operational band and configure the scenario.
     */
    BandwidthPartInfoPtrVector allBwps;
    CcBwpCreator ccBwpCreator;
    const uint8_t numCcPerBand = 1; // in this example we have a single band, and that band is
                                    // composed of a single component carrier

    /* Create the configuration for the CcBwpHelper. SimpleOperationBandConf creates
     * a single BWP per CC and a single BWP in CC.
     *
     * Hence, the configured spectrum is:
     *
     * |---------------Band---------------|
     * |---------------CC-----------------|
     * |---------------BWP----------------|
     */
    CcBwpCreator::SimpleOperationBandConf bandConf(frequency,
                                                   bandwidth,
                                                   numCcPerBand,
                                                   scenarioEnum);
    OperationBandInfo band = ccBwpCreator.CreateOperationBandContiguousCc(bandConf);
    // Initialize channel and pathloss, plus other things inside band.
    nrHelper->InitializeOperationBand(&band);
    allBwps = CcBwpCreator::GetAllBwps({band});

    // Configure ideal beamforming method
    idealBeamformingHelper->SetAttribute("BeamformingMethod",
                                         TypeIdValue(DirectPathBeamforming::GetTypeId()));

    // Configure scheduler
    nrHelper->SetSchedulerTypeId(NrMacSchedulerTdmaRR::GetTypeId());

    // Antennas for the UEs
    nrHelper->SetUeAntennaAttribute("NumRows", UintegerValue(2));
    nrHelper->SetUeAntennaAttribute("NumColumns", UintegerValue(4));
    nrHelper->SetUeAntennaAttribute("AntennaElement",
                                    PointerValue(CreateObject<IsotropicAntennaModel>()));

    // Antennas for the gNbs
    nrHelper->SetGnbAntennaAttribute("NumRows", UintegerValue(8));
    nrHelper->SetGnbAntennaAttribute("NumColumns", UintegerValue(8));
    nrHelper->SetGnbAntennaAttribute("AntennaElement",
                                     PointerValue(CreateObject<IsotropicAntennaModel>()));

    // install nr net devices
    NetDeviceContainer gnbNetDev = nrHelper->InstallGnbDevice(gnbNodes, allBwps);
    NetDeviceContainer ueNetDev = nrHelper->InstallUeDevice(ueNodes, allBwps);

    int64_t randomStream = 1;
    randomStream += nrHelper->AssignStreams(gnbNetDev, randomStream);
    randomStream += nrHelper->AssignStreams(ueNetDev, randomStream);

    nrHelper->GetGnbPhy(gnbNetDev.Get(0), 0)->SetTxPower(txPower);
    nrHelper->GetGnbPhy(gnbNetDev.Get(1), 0)->SetTxPower(txPower);

    // When all the configuration is done, explicitly call UpdateConfig ()
    for (auto it = gnbNetDev.Begin(); it != gnbNetDev.End(); ++it)
    {
        DynamicCast<NrGnbNetDevice>(*it)->UpdateConfig();
    }

    for (auto it = ueNetDev.Begin(); it != ueNetDev.End(); ++it)
    {
        DynamicCast<NrUeNetDevice>(*it)->UpdateConfig();
    }

    // create the internet and install the IP stack on the UEs
    // get SGW/PGW and create a single RemoteHost
    Ptr<Node> pgw = nrEpcHelper->GetPgwNode();
    NodeContainer remoteHostContainer;
    remoteHostContainer.Create(1);
    Ptr<Node> remoteHost = remoteHostContainer.Get(0);
    InternetStackHelper internet;
    internet.Install(remoteHostContainer);

    // connect a remoteHost to pgw. Setup routing too
    PointToPointHelper p2ph;
    p2ph.SetDeviceAttribute("DataRate", DataRateValue(DataRate("100Gb/s")));
    p2ph.SetDeviceAttribute("Mtu", UintegerValue(2500));
    p2ph.SetChannelAttribute("Delay", TimeValue(Seconds(0.010)));
    NetDeviceContainer internetDevices = p2ph.Install(pgw, remoteHost);

    Ipv4AddressHelper ipv4h;
    ipv4h.SetBase("1.0.0.0", "255.0.0.0");
    Ipv4InterfaceContainer internetIpIfaces = ipv4h.Assign(internetDevices);
    Ipv4StaticRoutingHelper ipv4RoutingHelper;

    Ptr<Ipv4StaticRouting> remoteHostStaticRouting =
        ipv4RoutingHelper.GetStaticRouting(remoteHost->GetObject<Ipv4>());
    remoteHostStaticRouting->AddNetworkRouteTo(Ipv4Address("7.0.0.0"), Ipv4Mask("255.0.0.0"), 1);
    internet.Install(ueNodes);

    Ipv4InterfaceContainer ueIpIface;
    ueIpIface = nrEpcHelper->AssignUeIpv4Address(NetDeviceContainer(ueNetDev));

    // assign IP address to UEs, and install UDP downlink applications
    uint16_t dlPort = 1234;
    ApplicationContainer clientApps;
    ApplicationContainer serverApps;
    for (uint32_t u = 0; u < ueNodes.GetN(); ++u)
    {
        Ptr<Node> ueNode = ueNodes.Get(u);
        // Set the default gateway for the UE
        Ptr<Ipv4StaticRouting> ueStaticRouting =
            ipv4RoutingHelper.GetStaticRouting(ueNode->GetObject<Ipv4>());
        ueStaticRouting->SetDefaultRoute(nrEpcHelper->GetUeDefaultGatewayAddress(), 1);

        UdpServerHelper dlPacketSinkHelper(dlPort);
        serverApps.Add(dlPacketSinkHelper.Install(ueNodes.Get(u)));

        UdpClientHelper dlClient(ueIpIface.GetAddress(u), dlPort);
        dlClient.SetAttribute("Interval", TimeValue(MicroSeconds(1)));
        // dlClient.SetAttribute ("MaxPackets", UintegerValue(0xFFFFFFFF));
        dlClient.SetAttribute("MaxPackets", UintegerValue(10));
        dlClient.SetAttribute("PacketSize", UintegerValue(1500));
        clientApps.Add(dlClient.Install(remoteHost));
    }

    // attach UEs to the closest gNB
    nrHelper->AttachToClosestGnb(ueNetDev, gnbNetDev);

    // start server and client apps
    serverApps.Start(Seconds(0.4));
    clientApps.Start(Seconds(0.4));
    serverApps.Stop(Seconds(simTime));
    clientApps.Stop(Seconds(simTime - 0.2));

    // enable the traces provided by the nr module
    nrHelper->EnableTraces();

    AnimationInterface anim("3gpp-channel.xml");

    Simulator::Stop(Seconds(simTime));
    Simulator::Run();

    Ptr<UdpServer> serverApp = serverApps.Get(0)->GetObject<UdpServer>();
    uint64_t receivedPackets = serverApp->GetReceived();

    Simulator::Destroy();

    if (receivedPackets == 10)
    {
        return EXIT_SUCCESS;
    }
    else
    {
        return EXIT_FAILURE;
    }
}

Key Components Explained

Node and Mobility Setup

NodeContainer gnbNodes;
NodeContainer ueNodes;
gnbNodes.Create(2);
ueNodes.Create(2);

Nodes are placed using ListPositionAllocator and controlled by MobilityHelper, which determines if the UEs are static or moving.

Helper Classes

Helpers simplify the creation of complex ns-3 objects.
In this example:

Helper Purpose
NodeContainer Creates and manages multiple nodes.
MobilityHelper Assigns mobility or static positions to nodes.
NrHelper Installs 5G gNB/UE devices and configures PHY/MAC layers.
NrPointToPointEpcHelper Sets up the EPC (core) network and connects to the Internet.
IdealBeamformingHelper Simulates 5G directional beamforming.
CcBwpCreator Defines carrier bandwidth parts (BWPs) and frequency bands.
ThreeGppChannelHelper Models 3GPP channel propagation characteristics.

These helpers abstract away internal complexity, allowing you to focus on system-level design instead of wiring individual components.

EPC and Internet Configuration

Ptr<NrPointToPointEpcHelper> nrEpcHelper = CreateObject<NrPointToPointEpcHelper>();
Ptr<Node> pgw = nrEpcHelper->GetPgwNode();

A Point-to-Point link connects the PGW to a remote host, simulating the Internet backbone. The IPv4 stack and static routing are configured for connectivity.

Applications

Each UE runs a UDP server, and the remote host sends 10 packets:

UdpClientHelper dlClient(ueIpIface.GetAddress(u), dlPort);
dlClient.SetAttribute("MaxPackets", UintegerValue(10));
dlClient.SetAttribute("PacketSize", UintegerValue(1500));

This allows performance evaluation (throughput, delay, etc.) across the simulated 5G channel.

Visualization

AnimationInterface anim("3gpp-channel.xml");

The AnimationInterface exports simulation data for NetAnim, allowing visual inspection of node mobility and packet exchanges.
NetAnim provides a graphical way to analyze your ns-3 simulation. Once your script generates 3gpp-channel.xml, follow these steps:

Install Qt (required for NetAnim GUI):
brew install qt
Compile NetAnim
cd netanim-3.42
qmake NetAnim.pro
make
Run NetAnim:
./NetAnim

Launch NetAnim, and you’ll see the initial interface.
image

NetAnim provides three different views of the simulation:

  • Animator: Displays node positions, movements, and packet animations. The network topology, including nodes and their links, becomes visible.
    Click the file icon (top-left) and select 3gpp-channel.xml, you can see as following.
    image

  • Stats: Shows statistics for nodes.
    image

  • Packets: Visualizes packet flows between nodes.
    image

NetAnim allows you to confirm topology correctness and visually inspect the packet flow throughout the simulation.

Conclusion

ns-3 is a powerful and flexible simulation framework capable of modeling a wide range of network technologies. Its modular architecture allows researchers and developers to easily combine different layers of the protocol stack, experiment with realistic mobility and propagation models, and evaluate system performance under diverse scenarios.

In this guide, we introduced the fundamentals of ns-3 and also explored a practical example using the 5G LENA module to simulate a 3GPP-compliant 5G network, complete with EPC integration, beamforming, and traffic modeling.

To deepen your understanding, explore the official examples/ directory, which contains well-documented sample scripts covering various protocols and use cases. You can also explore the contrib/ directory to clone and integrate additional modules such as 5G LENA or mmWave.

By experimenting with these examples and modules, you’ll discover how ns-3 can serve as a robust research and prototyping platform for next-generation communication systems.

Reference

About me

Hi, I'm Peggy Cheng, a newcomer to 5G and the free5GC community. Feel free to tell me if there's any mistake in the article.

Connect with Me

GitHub: HiImPeggy