Skip to content

Session 5

UDP over Wi-Fi with tracing and pcap

Updated View as Markdown

This session sends UDP traffic across the wireless network from Session 4 and records what arrives, both as a plot of bytes over time and as a pcap trace of the receiver’s Wi-Fi interface.

Objectives

Do not copy. Read for understanding and the viva
  • Complete questions 10 to 12 of the manual: udp over wi-fi with tracing and pcap
  • Prepare the deliverable before the lab and finish it during the session
  • Be ready to explain every step in the viva

Questions Covered

Do not copy. Read for understanding and the viva
QuestionRequirementStatus
Q10Create a UDP client on a node n1 and a UDP server on a node n2Complete
Q11Send packets to node n2 from node n1 and plot the number of bytes received with…Complete
Q12Show the pcap traces at node n2’s Wi-Fi interfaceComplete

Preparation

Do not copy. Read for understanding and the viva
  • Attach a trace to the sink’s Rx callback and accumulate bytes per second into a file; plot with gnuplot or matplotlib.
  • YansWifiPhyHelper::EnablePcap("node2", devices.Get(1)) writes the Wi-Fi pcap for node n2 only.
  • In Wireshark, filter on udp and use Statistics, IO Graph to cross-check your plot.

Question 10

Problem Statement

Write in lab record

Create a UDP client on a node n1 and a UDP server on a node n2.

Solution

Write in lab record

Questions 10, 11 and 12 share one program, wifi_udp_trace.cc: question 10 is the network and the two applications, question 11 the Rx trace and the plot, question 12 the pcap. The code indices 0 and 1 are the manual’s n1 and n2. Positions are fixed 50 m apart so the plot is repeatable; the OLSR routing from Session 4 stays in place.

Steps

  1. Save the program as scratch/wifi_udp_trace.cc and run ./ns3 run scratch/wifi_udp_trace.
  2. Check the last two console lines: 900 packets received, 0 lost, 921600 bytes.
  3. Rate check with the formula sheet: 1024 bytes every 10 ms is R=8L/Δt=8192/0.01=819.2kbit/s, well under the 11 Mbit/s channel.
  4. Try --interval=1 (1 ms, 8.19 Mbit/s) and watch lost become non-zero as the channel saturates.

Program

wifi_udp_trace.cccpp
/*
 * MCSL-223 Section 1, Session 5, Questions 10, 11 and 12
 * UDP client on n1, UDP server on n2 over the Session 4 ad-hoc Wi-Fi.
 * Writes bytes-received-versus-time to rx-bytes.dat and a pcap of n2's
 * Wi-Fi interface.
 *
 * Build: copy to ns-3.36+/scratch/ and run
 *   ./ns3 run scratch/wifi_udp_trace
 *   gnuplot rx_bytes.plt          (plot)
 *   wireshark wifi-udp-1-0.pcap   (n2 trace)
 *
 *   n1 (10.1.1.1, UdpClient) ---- 50 m ---- n2 (10.1.1.2, UdpServer port 9)
 *   Node index 0 and 1 in the code = n1 and n2 in the manual.
 *   Positions are fixed so the plot is repeatable; the Session 4 OLSR
 *   setup is kept so a third node could forward if added.
 */
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"
#include "ns3/olsr-module.h"
#include "ns3/applications-module.h"
#include <fstream>

using namespace ns3;

NS_LOG_COMPONENT_DEFINE("WifiUdpTrace");

static uint64_t g_rxBytes = 0;      // bytes received at n2 so far
static std::ofstream g_traceFile;   // rx-bytes.dat

/*
 * RxTrace: called by the UdpServer "Rx" trace source for every packet
 * that reaches the application on n2. Adds its size to the running total.
 */
static void
RxTrace(Ptr<const Packet> packet)
{
    g_rxBytes += packet->GetSize();
}

/*
 * SampleRx: writes "time  cumulative_bytes" once per sample interval so
 * gnuplot can draw bytes received against time.
 */
static void
SampleRx(double interval)
{
    g_traceFile << Simulator::Now().GetSeconds() << "\t" << g_rxBytes << std::endl;
    Simulator::Schedule(Seconds(interval), &SampleRx, interval);
}

/*
 * main: builds the two-node ad-hoc Wi-Fi link (Q10), installs UdpServer on
 * n2 and UdpClient on n1 at 1024 B every 10 ms, records bytes over time
 * (Q11) and a pcap on n2's Wi-Fi device (Q12).
 */
int
main(int argc, char* argv[])
{
    double simTime = 10.0;
    uint32_t packetSize = 1024;
    double intervalMs = 10.0;     // 1024 B / 10 ms = 819.2 kbit/s
    double sample = 0.5;          // trace sample period in seconds
    double distance = 50.0;

    CommandLine cmd(__FILE__);
    cmd.AddValue("simTime", "Simulation length in seconds", simTime);
    cmd.AddValue("packetSize", "UDP payload in bytes", packetSize);
    cmd.AddValue("interval", "Client send interval in ms", intervalMs);
    cmd.AddValue("distance", "Distance between n1 and n2 in metres", distance);
    cmd.Parse(argc, argv);

    LogComponentEnable("UdpServer", LOG_LEVEL_INFO);

    // ---- Q10: two nodes on an ad-hoc Wi-Fi channel ----
    NodeContainer nodes;
    nodes.Create(2);

    YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
    YansWifiPhyHelper phy;
    phy.SetChannel(channel.Create());

    WifiHelper wifi;
    wifi.SetStandard(WIFI_STANDARD_80211b);
    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                 "DataMode", StringValue("DsssRate11Mbps"),
                                 "ControlMode", StringValue("DsssRate11Mbps"));
    WifiMacHelper mac;
    mac.SetType("ns3::AdhocWifiMac");
    NetDeviceContainer devices = wifi.Install(phy, mac, nodes);

    // Fixed positions: n1 at the origin, n2 `distance` metres along x.
    Ptr<ListPositionAllocator> positions = CreateObject<ListPositionAllocator>();
    positions->Add(Vector(0.0, 0.0, 0.0));
    positions->Add(Vector(distance, 0.0, 0.0));
    MobilityHelper mobility;
    mobility.SetPositionAllocator(positions);
    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
    mobility.Install(nodes);

    OlsrHelper olsr;
    InternetStackHelper stack;
    stack.SetRoutingHelper(olsr);
    stack.Install(nodes);

    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.0");
    Ipv4InterfaceContainer interfaces = address.Assign(devices);

    // UDP server on n2 (port 9) and UDP client on n1.
    uint16_t port = 9;
    UdpServerHelper server(port);
    ApplicationContainer serverApp = server.Install(nodes.Get(1));
    serverApp.Start(Seconds(0.5));
    serverApp.Stop(Seconds(simTime));

    UdpClientHelper client(interfaces.GetAddress(1), port);
    client.SetAttribute("MaxPackets", UintegerValue(4294967295u)); // until Stop
    client.SetAttribute("Interval", TimeValue(MicroSeconds(static_cast<uint64_t>(intervalMs * 1000))));
    client.SetAttribute("PacketSize", UintegerValue(packetSize));
    ApplicationContainer clientApp = client.Install(nodes.Get(0));
    clientApp.Start(Seconds(1.0));
    clientApp.Stop(Seconds(simTime));

    // ---- Q11: bytes received versus time ----
    Ptr<UdpServer> udpServer = DynamicCast<UdpServer>(serverApp.Get(0));
    udpServer->TraceConnectWithoutContext("Rx", MakeCallback(&RxTrace));
    g_traceFile.open("rx-bytes.dat");
    g_traceFile << "# time(s)\tbytes_received_at_n2" << std::endl;
    Simulator::Schedule(Seconds(0.0), &SampleRx, sample);

    // ---- Q12: pcap on n2's Wi-Fi interface only -> wifi-udp-1-0.pcap ----
    phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
    phy.EnablePcap("wifi-udp", devices.Get(1));

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

    std::cout << "Packets received at n2: " << udpServer->GetReceived()
              << ", lost: " << udpServer->GetLost() << std::endl;
    std::cout << "Bytes received at n2  : " << g_rxBytes << " in " << simTime - 1.0
              << " s = " << g_rxBytes * 8.0 / (simTime - 1.0) / 1e3 << " kbit/s" << std::endl;

    g_traceFile.close();
    Simulator::Destroy();
    return 0;
}

Output

Expected console output. UdpServer logs one line per packet; the delay per packet is the Wi-Fi hop delay computed in Session 4 (about 1.3 ms for a 1024-byte frame), so time stamps are illustrative.

TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 0 Uid: 6 TXtime: +1s RXtime: +1.00134s Delay: +0.00134s
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 1 Uid: 8 TXtime: +1.01s RXtime: +1.01134s Delay: +0.00134s
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 2 Uid: 10 TXtime: +1.02s RXtime: +1.02134s Delay: +0.00134s
...
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 899 Uid: 1804 TXtime: +9.99s RXtime: +9.99134s Delay: +0.00134s
Packets received at n2: 900, lost: 0
Bytes received at n2  : 921600 in 9 s = 819.2 kbit/s

Explanation

UdpServerHelper(9) installs a UdpServer that binds UDP port 9, counts packets by the sequence number UdpClient puts in a 12-byte SeqTsHeader, and reports lost ones from gaps in the sequence. UdpClientHelper on n1 sends PacketSize bytes every Interval; MaxPackets is set to the maximum so the Stop time ends the flow. Unlike the echo pair of Session 1 this is one-way traffic, which is what the plot in question 11 needs. The two nodes are one hop apart, so OLSR only fills in the direct route.

Question 11

Problem Statement

Write in lab record

Send packets to node n2 from node n1 and plot the number of bytes received with respect to time at node n2.

Solution

Write in lab record

Steps

  1. In the program, RxTrace is connected to the server’s Rx trace source with TraceConnectWithoutContext; it adds each packet size to a counter.
  2. SampleRx runs every 0.5 s and appends time bytes to rx-bytes.dat.
  3. After the run, plot with gnuplot rx_bytes.plt; it writes rx-bytes.png.
  4. Paste the plot in the record with the axis labels and the two numbers it must agree with: 0 bytes until 1 s, 921600 bytes at 10 s.

Program

The trace part of wifi_udp_trace.cc:

static void RxTrace(Ptr<const Packet> packet) { g_rxBytes += packet->GetSize(); }

static void SampleRx(double interval)
{
    g_traceFile << Simulator::Now().GetSeconds() << "\t" << g_rxBytes << std::endl;
    Simulator::Schedule(Seconds(interval), &SampleRx, interval);
}

Ptr<UdpServer> udpServer = DynamicCast<UdpServer>(serverApp.Get(0));
udpServer->TraceConnectWithoutContext("Rx", MakeCallback(&RxTrace));

The gnuplot script:

rx_bytes.plttext
# gnuplot script: bytes received at n2 against time
# usage: gnuplot rx_bytes.plt   (reads rx-bytes.dat written by wifi_udp_trace)
set terminal pngcairo size 800,500
set output "rx-bytes.png"
set title "UDP bytes received at n2 (Session 5)"
set xlabel "Time (s)"
set ylabel "Cumulative bytes received"
set grid
set key left top
plot "rx-bytes.dat" using 1:2 with linespoints lw 2 pt 7 title "bytes at n2"

Output

Expected rx-bytes.dat (every packet arrives 1.34 ms after it is sent, so the sample at each half second counts 50 packets more than the previous one; the sample at 10 s is not written because the simulator stops first):

# time(s)	bytes_received_at_n2
0	0
0.5	0
1	0
1.5	51200
2	102400
2.5	153600
3	204800
3.5	256000
4	307200
4.5	358400
5	409600
5.5	460800
6	512000
6.5	563200
7	614400
7.5	665600
8	716800
8.5	768000
9	819200
9.5	870400

The plot is a straight line from (1 s, 0) to (10 s, 921600) with slope 921600/9=102400bytes/s=819.2kbit/s, the offered rate. A flat section would mean the channel was busy or the nodes moved out of range.

Explanation

NS-3 tracing separates the event source from the consumer. UdpServer fires its Rx trace source for every packet it receives; the script attaches a plain C++ function to it with TraceConnectWithoutContext (the same mechanism Config::ConnectWithoutContext uses with a path string, as in Session 8 for the congestion window). Sampling on a timer instead of writing per packet keeps the file small and gives evenly spaced points, which gnuplot draws directly with using 1:2. The throughput formula from the sheet is the slope of this line: bytes received divided by elapsed time, times 8.

Question 12

Problem Statement

Write in lab record

Show the pcap traces at node n2’s Wi-Fi interface.

Solution

Write in lab record

Steps

  1. The program calls phy.EnablePcap("wifi-udp", devices.Get(1)), which captures only n2’s Wi-Fi device and writes wifi-udp-1-0.pcap (node 1, device 0).
  2. SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO) adds a radiotap header so Wireshark shows the data rate and signal strength of every frame.
  3. Open it: wireshark wifi-udp-1-0.pcap.
  4. Apply the filter udp.dstport == 9 to see only the client’s datagrams; wlan.fc.type_subtype == 0x1d shows the 802.11 ACKs n2 sent back; olsr shows the routing traffic.
  5. Statistics, I/O Graph with the filter udp.dstport == 9 and Y axis set to Bytes reproduces the plot from question 11 straight from the capture.

Output

Expected Wireshark packet list for wifi-udp-1-0.pcap with filter udp.dstport == 9 (900 frames displayed):

No.   Time      Source     Destination  Protocol Length Info
12    1.001340  10.1.1.1   10.1.1.2     UDP      1112   49153 → 9 Len=1024
14    1.011340  10.1.1.1   10.1.1.2     UDP      1112   49153 → 9 Len=1024
16    1.021340  10.1.1.1   10.1.1.2     UDP      1112   49153 → 9 Len=1024
...
1810  9.991340  10.1.1.1   10.1.1.2     UDP      1112   49153 → 9 Len=1024

Frame length 1112 is 1024 payload + 8 UDP + 20 IP + 8 LLC/SNAP + 24 802.11 header + 4 FCS = 1088 bytes on air, plus the radiotap header NS-3 writes (24 bytes here; the exact size varies by version). Without the filter the list also contains OLSR HELLO messages every 2 s from both nodes and the 802.11 ACK n2 returns after each data frame. Wireshark’s Statistics, Capture File Properties shows about 921600 bytes of UDP payload and an average of 100 packets per second.

Explanation

YansWifiPhyHelper::EnablePcap hooks the phy’s MonitorSnifferRx and MonitorSnifferTx trace sources on the given device only, so the file holds every frame n2’s radio sent or received, including frames that were not for it. That is the wireless equivalent of a network tap and is why the manual asks for the trace at n2 rather than n1: it shows what arrived, not what was sent. The radiotap link type is worth enabling because the plain 802.11 format drops the rate and signal fields. The three views from this session agree with each other: the server’s packet count, the trace file’s slope and the pcap byte total are the same 921600 bytes measured at the application, the trace and the radio.

Viva Questions

Do not copy. Read for understanding and the viva
  • Q: What is the difference between UdpClient and UdpEchoClient? A: UdpClient sends one way with a sequence and time stamp header; UdpEchoClient expects a reply.
  • Q: How does UdpServer know a packet was lost? A: From a gap in the sequence numbers carried in the SeqTsHeader.
  • Q: What is a trace source and a trace sink? A: The source is the event NS-3 fires, the sink is the function you attach with TraceConnect.
  • Q: Why sample every 0.5 s instead of logging each packet? A: Fewer, evenly spaced points; the plot is the same line.
  • Q: What does the slope of the bytes-versus-time line mean? A: Throughput in bytes per second; times 8 gives bit/s.
  • Q: Why does the pcap file name contain 1-0? A: Node index 1 (n2), device index 0 on that node.
  • Q: What does the radiotap header add? A: Per-frame data rate, channel and signal strength for Wireshark to display.
  • Q: Why do OLSR packets appear in the trace when only UDP data was sent? A: OLSR broadcasts HELLO and TC messages periodically regardless of data traffic.

Common Mistakes

Do not copy. Read for understanding and the viva
  • Connecting the Rx trace before the server application exists, or with a wrong Config path, so the counter stays at zero.
  • Passing a fraction to MilliSeconds; it truncates to an integer, so 0.5 becomes 0 ms and the client floods the channel. Use MicroSeconds.
  • Leaving MaxPackets at its default of 100, so the flow stops after one second and the plot goes flat.
  • Calling EnablePcap on devices (all nodes) and then opening n1’s file to show n2’s interface.
  • Forgetting the # header line in the data file and getting a parse error from gnuplot on the label row.
  • Reporting the pcap byte count (with headers) as the received bytes instead of the application count.

Formula Sheet

Do not copy. Read for understanding and the viva

Throughput

Bytes received at the sink divided by the time the flow was active, converted to bits per second:

Throughput=8×bytes receivedtend−tstartbit/s

Bandwidth-delay product

The amount of data in flight on a link of capacity B and round-trip time RTT. A TCP window smaller than this cannot fill the link:

BDP=B×RTT

For the Session 10 defaults, B=5Mbit/s and one-way delay 5ms give RTT=10ms and BDP=5×106×0.010=50,000bits≈6.25kB.

Transmission and propagation delay

ttrans=LB,tprop=dv,ttotal=ttrans+tprop+tqueue+tproc

where L is packet size in bits, B link rate, d distance and v signal speed (about 2×108m/s in copper or fibre).

Constant bit rate traffic

An OnOff application with packet size L bytes and rate R bit/s sends one packet every

Δt=8LR seconds

TCP congestion window

Slow start doubles the window every RTT until the threshold; congestion avoidance adds one segment per RTT; a loss halves it (TCP NewReno):

cwndss(n)=2nMSS,cwndca←cwnd+MSS2cwnd,cwndloss←cwnd2

The maximum throughput of one TCP flow is bounded by

Throughputmax⁡=cwndmax⁡RTT

Packet loss

Loss rate=packets sent−packets receivedpackets sent

Session Summary

Write in lab record
  • wifi_udp_trace.cc listing, the UdpServer log and the final packet and byte counts
  • rx-bytes.dat, rx_bytes.plt and the bytes-versus-time plot with its slope
  • Wireshark view of wifi-udp-1-0.pcap filtered on udp.dstport == 9 and the I/O Graph
Navigation

Type to search…

↑↓ navigate↵ selectEsc close