A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-bbr-example.py
Go to the documentation of this file.
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Copyright (c) 2018-20 NITK Surathkal
4# Copyright (c) 2025 CSPIT, CHARUSAT
5#
6# Authors:
7# Aarti Nandagiri <aarti.nandagiri@gmail.com>
8# Vivek Jain <jain.vivek.anand@gmail.com>
9# Mohit P. Tahiliani <tahiliani@nitk.edu.in>
10# Urval Kheni <kheniurval777@gmail.com>
11# Ritesh Patel <riteshpatel.ce@charusat.ac.in>
12
13"""
14Python port of tcp-bbr-example.cc
15
16Simulates the following topology:
17
18 Sender ---- R1 ---- R2 ---- Receiver
19 1000Mbps 10Mbps 1000Mbps
20 5ms 10ms 5ms
21
22Outputs:
23- cwnd.dat (congestion window)
24- throughput.dat (throughput)
25- queueSize.dat (queue length)
26"""
27
28import argparse
29import os
30import time
31
32try:
33 from ns import ns
34except ModuleNotFoundError:
35 raise SystemExit(
36 "Error: ns3 Python module not found. "
37 "Bindings may not be enabled or PYTHONPATH is incorrect."
38 )
39
40# Global trace variables
41prev_tx = 0
42prev_time = None
43throughput_file = None
44queue_file = None
45cwnd_file = None
46outdir = ""
47
48
50 """Throughput trace callback."""
51 global prev_tx, prev_time, throughput_file
52
53 stats = ns.cppyy.gbl.monitor_ptr.GetFlowStats()
54 if stats and not stats.empty():
55 flow_stats = stats.begin().__deref__().second
56 cur_time = ns.Now()
57
58 bits = 8 * (flow_stats.txBytes - prev_tx)
59 us = (cur_time - prev_time).ToDouble(ns.Time.US)
60
61 if us > 0:
62 throughput_file.write(f"{cur_time.GetSeconds():.6g}s {bits / us:.6g} Mbps\n")
63 throughput_file.flush()
64
65 prev_time = cur_time
66 prev_tx = flow_stats.txBytes
67
68
70 """Queue length tracer."""
71 global queue_file
72
73 q = ns.cppyy.gbl.qdisc_ptr.GetCurrentSize().GetValue()
74 queue_file.write(f"{ns.Simulator.Now().GetSeconds():.6g} {q}\n")
75 queue_file.flush()
76
77
78def trace_cwnd_callback(oldval, newval):
79 """Congestion window tracer."""
80 global cwnd_file
81
82 cwnd_file.write(f"{ns.Simulator.Now().GetSeconds():.6g} {newval / 1448.0:.6g}\n")
83 cwnd_file.flush()
84
85
86# C++ helper functions for scheduling Python callbacks
87ns.cppyy.cppdef(r"""
88#include "ns3/simulator.h"
89#include "ns3/flow-monitor-helper.h"
90#include "ns3/queue-disc.h"
91#include "ns3/node.h"
92#include "ns3/config.h"
93
94using namespace ns3;
95
96Ptr<FlowMonitor> monitor_ptr;
97Ptr<QueueDisc> qdisc_ptr;
98
99// Helper to create event from Python callback without parameters
100EventImpl* pythonMakeEvent(void (*f)())
101{
102 return MakeEvent(f);
103}
104
105// Helper to connect cwnd callback and create event
106EventImpl* pythonConnectCwndTrace(void (*f)(uint32_t, uint32_t))
107{
108 Config::ConnectWithoutContext(
109 "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
110 MakeCallback(f));
111 return nullptr;
112}
113""")
114
115
116def main():
117 global throughput_file, queue_file, cwnd_file, outdir, prev_time
118 global prev_tx
119
120 parser = argparse.ArgumentParser(description="tcp-bbr-example (Python)")
121 parser.add_argument(
122 "--tcpTypeId",
123 default="TcpBbr",
124 help="Transport protocol: TcpNewReno, TcpBbr",
125 )
126 parser.add_argument("--delAckCount", type=int, default=2, help="Delayed ACK count")
127 parser.add_argument("--enablePcap", action="store_true", help="Enable pcap traces")
128 parser.add_argument("--stopTime", type=float, default=100.0, help="Simulation stop time")
129 args = parser.parse_args()
130
131 ts = time.strftime("%d-%m-%Y-%I-%M-%S")
132 outdir = f"bbr-results/{ts}/"
133 os.makedirs(outdir, exist_ok=True)
134
135 tcpType = args.tcpTypeId
136 queueDisc = "ns3::FifoQueueDisc"
137 delAck = args.delAckCount
138 enablePcap = args.enablePcap
139 stopTime = ns.Seconds(args.stopTime)
140
141 ns.Config.SetDefault(
142 "ns3::TcpL4Protocol::SocketType",
143 ns.StringValue("ns3::" + tcpType),
144 )
145 ns.Config.SetDefault("ns3::TcpSocket::SndBufSize", ns.UintegerValue(4194304))
146 ns.Config.SetDefault("ns3::TcpSocket::RcvBufSize", ns.UintegerValue(6291456))
147 ns.Config.SetDefault("ns3::TcpSocket::InitialCwnd", ns.UintegerValue(10))
148 ns.Config.SetDefault("ns3::TcpSocket::DelAckCount", ns.UintegerValue(delAck))
149 ns.Config.SetDefault("ns3::TcpSocket::SegmentSize", ns.UintegerValue(1448))
150 ns.Config.SetDefault(
151 "ns3::DropTailQueue<Packet>::MaxSize", ns.QueueSizeValue(ns.QueueSize("1p"))
152 )
153 ns.Config.SetDefault(
154 queueDisc + "::MaxSize",
155 ns.QueueSizeValue(ns.QueueSize("100p")),
156 )
157
158 sender = ns.NodeContainer()
159 receiver = ns.NodeContainer()
160 routers = ns.NodeContainer()
161 sender.Create(1)
162 receiver.Create(1)
163 routers.Create(2)
164
165 bottleneck = ns.PointToPointHelper()
166 bottleneck.SetDeviceAttribute("DataRate", ns.StringValue("10Mbps"))
167 bottleneck.SetChannelAttribute("Delay", ns.StringValue("10ms"))
168
169 edge = ns.PointToPointHelper()
170 edge.SetDeviceAttribute("DataRate", ns.StringValue("1000Mbps"))
171 edge.SetChannelAttribute("Delay", ns.StringValue("5ms"))
172
173 senderEdge = edge.Install(sender.Get(0), routers.Get(0))
174 r1r2 = bottleneck.Install(routers.Get(0), routers.Get(1))
175 receiverEdge = edge.Install(routers.Get(1), receiver.Get(0))
176
177 inet = ns.InternetStackHelper()
178 inet.Install(sender)
179 inet.Install(receiver)
180 inet.Install(routers)
181
182 tch = ns.TrafficControlHelper()
183 tch.SetRootQueueDisc(queueDisc)
184 tch.SetQueueLimits("ns3::DynamicQueueLimits", "HoldTime", ns.StringValue("1000ms"))
185 tch.Install(senderEdge)
186 tch.Install(receiverEdge)
187
188 ipv4 = ns.Ipv4AddressHelper()
189 ipv4.SetBase(ns.Ipv4Address("10.0.0.0"), ns.Ipv4Mask("255.255.255.0"))
190
191 _ = ipv4.Assign(r1r2)
192
193 ipv4.NewNetwork()
194 ipv4.Assign(senderEdge)
195
196 ipv4.NewNetwork()
197 ir1 = ipv4.Assign(receiverEdge)
198
199 ns.Ipv4GlobalRoutingHelper.PopulateRoutingTables()
200
201 port = 50001
202
203 source = ns.BulkSendHelper(
204 "ns3::TcpSocketFactory",
205 ns.InetSocketAddress(ir1.GetAddress(1), port).ConvertTo(),
206 )
207 source.SetAttribute("MaxBytes", ns.UintegerValue(0))
208 sourceApps = source.Install(sender.Get(0))
209 sourceApps.Start(ns.Seconds(0.1))
210 sourceApps.Stop(stopTime)
211
212 sink = ns.PacketSinkHelper(
213 "ns3::TcpSocketFactory",
214 ns.InetSocketAddress(ns.Ipv4Address.GetAny(), port).ConvertTo(),
215 )
216 sinkApps = sink.Install(receiver.Get(0))
217 sinkApps.Start(ns.Seconds(0))
218 sinkApps.Stop(stopTime)
219
220 if enablePcap:
221 os.makedirs(os.path.join(outdir, "pcap"), exist_ok=True)
222
223 throughput_file = open(os.path.join(outdir, "throughput.dat"), "w")
224 queue_file = open(os.path.join(outdir, "queueSize.dat"), "w")
225 cwnd_file = open(os.path.join(outdir, "cwnd.dat"), "w")
226
227 if enablePcap:
228 bottleneck.EnablePcapAll(os.path.join(outdir, "pcap", "bbr"), True)
229
230 flowmon = ns.FlowMonitorHelper()
231 monitor = flowmon.InstallAll()
232
233 tch.Uninstall(routers.Get(0).GetDevice(1))
234 qdc = tch.Install(routers.Get(0).GetDevice(1))
235
236 prev_time = ns.Now()
237 prev_tx = 0
238
239 ns.cppyy.gbl.monitor_ptr = monitor
240 ns.cppyy.gbl.qdisc_ptr = qdc.Get(0)
241
242 # Define wrapper functions that reschedule themselves
243 def throughput_tracer():
244 """Periodic throughput tracer that reschedules itself."""
246 event = ns.cppyy.gbl.pythonMakeEvent(throughput_tracer)
247 ns.Simulator.Schedule(ns.Seconds(0.2), event)
248
249 def queue_size_tracer():
250 """Periodic queue size tracer that reschedules itself."""
252 event = ns.cppyy.gbl.pythonMakeEvent(queue_size_tracer)
253 ns.Simulator.Schedule(ns.Seconds(0.2), event)
254
255 # Schedule initial events using pythonMakeEvent
256 event = ns.cppyy.gbl.pythonMakeEvent(throughput_tracer)
257 ns.Simulator.Schedule(ns.Seconds(0.000001), event)
258
259 event = ns.cppyy.gbl.pythonMakeEvent(queue_size_tracer)
260 ns.Simulator.ScheduleNow(event)
261
262 # Connect cwnd trace after socket is created
263 def connect_cwnd_trace():
264 """Connect the cwnd trace callback."""
265 ns.cppyy.gbl.pythonConnectCwndTrace(trace_cwnd_callback)
266
267 event = ns.cppyy.gbl.pythonMakeEvent(connect_cwnd_trace)
268 ns.Simulator.Schedule(ns.Seconds(0.1) + ns.MilliSeconds(1), event)
269
270 ns.Simulator.Stop(stopTime + ns.TimeStep(1))
271 ns.Simulator.Run()
272 ns.Simulator.Destroy()
273
274 throughput_file.close()
275 queue_file.close()
276 cwnd_file.close()
277
278
279if __name__ == "__main__":
280 main()
trace_cwnd_callback(oldval, newval)