A Discrete-Event Network Simulator
API
tcp-pacing.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 NITK Surathkal
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Authors: Vivek Jain <jain.vivek.anand@gmail.com>
18  * Deepak Kumaraswamy <deepakkavoor99@gmail.com>
19  */
20 
21 // The following network topology is used in this example, and is taken from
22 // Figure 2 of https://homes.cs.washington.edu/~tom/pubs/pacing.pdf
23 //
24 // n0 n4
25 // | |
26 // |(4x Mbps, 5ms) |(4x Mbps, 5ms)
27 // | |
28 // | |
29 // | (x Mbps, 40ms) |
30 // n2 ------------------------ n3
31 // | |
32 // | |
33 // |(4x Mbps, 5ms) |(4x Mbps, 5ms)
34 // | |
35 // n1 n5
36 //
37 //
38 
39 // This example illustrates how TCP pacing can be enabled on a socket.
40 // Two long-running TCP flows are instantiated at nodes n0 and n1 to
41 // send data over a bottleneck link (n2->n3) to sink nodes n4 and n5.
42 // At the end of the simulation, the IP-level flow monitor tool will
43 // print out summary statistics of the flows. The flow monitor detects
44 // four flows, but that is because the flow records are unidirectional;
45 // the latter two flows reported are actually ack streams.
46 //
47 // At the end of this simulation, data files are also generated
48 // that track changes in Congestion Window, Slow Start threshold and
49 // TCP pacing rate for the first flow (n0). Additionally, a data file
50 // that contains information about packet transmission and reception times
51 // (collected through TxTrace and RxTrace respectively) is also produced.
52 // This transmission and reception (ack) trace is the most direct way to
53 // observe the effects of pacing. All the above information is traced
54 // just for the single node n0.
55 //
56 // A small amount of randomness is introduced to the program to control
57 // the start time of the flows.
58 //
59 // This example has pacing enabled by default, which means that TCP
60 // does not send packets back-to-back, but instead paces them out over
61 // an RTT. The size of initial congestion window is set to 10, and pacing
62 // of the initial window is enabled. The available command-line options and
63 // their default values can be observed in the usual way by running the
64 // program to print the help info; i.e.: ./ns3 run 'tcp-pacing --PrintHelp'
65 //
66 // When pacing is disabled, TCP sends eligible packets back-to-back. The
67 // differences in behaviour when pacing is disabled can be observed from the
68 // packet transmission data file. For instance, one can observe that
69 // packets in the initial window are sent one after the other simultaneously,
70 // without any inter-packet gaps. Another instance is when n0 receives a
71 // packet in the form of an acknowledgement, and sends out data packets without
72 // pacing them.
73 //
74 // Although this example serves as a useful demonstration of how pacing could
75 // be enabled/disabled in ns-3 TCP congestion controls, we could not observe
76 // significant improvements in throughput for the above topology when pacing
77 // was enabled. In future, one could try and incorporate models such as
78 // TCP Prague and ACK-filtering, which may show a stronger performance
79 // impact for TCP pacing.
80 
81 #include "ns3/applications-module.h"
82 #include "ns3/core-module.h"
83 #include "ns3/flow-monitor-module.h"
84 #include "ns3/internet-module.h"
85 #include "ns3/ipv4-global-routing-helper.h"
86 #include "ns3/network-module.h"
87 #include "ns3/packet-sink.h"
88 #include "ns3/point-to-point-module.h"
89 #include "ns3/traffic-control-module.h"
90 
91 #include <fstream>
92 #include <iomanip>
93 #include <iostream>
94 #include <string>
95 
96 using namespace ns3;
97 
98 NS_LOG_COMPONENT_DEFINE("TcpPacingExample");
99 
100 std::ofstream cwndStream;
101 std::ofstream pacingRateStream;
102 std::ofstream ssThreshStream;
103 std::ofstream packetTraceStream;
104 
105 static void
106 CwndTracer(uint32_t oldval, uint32_t newval)
107 {
108  cwndStream << std::fixed << std::setprecision(6) << Simulator::Now().GetSeconds()
109  << std::setw(12) << newval << std::endl;
110 }
111 
112 static void
114 {
115  pacingRateStream << std::fixed << std::setprecision(6) << Simulator::Now().GetSeconds()
116  << std::setw(12) << newval.GetBitRate() / 1e6 << std::endl;
117 }
118 
119 static void
120 SsThreshTracer(uint32_t oldval, uint32_t newval)
121 {
122  ssThreshStream << std::fixed << std::setprecision(6) << Simulator::Now().GetSeconds()
123  << std::setw(12) << newval << std::endl;
124 }
125 
126 static void
127 TxTracer(Ptr<const Packet> p, Ptr<Ipv4> ipv4, uint32_t interface)
128 {
129  packetTraceStream << std::fixed << std::setprecision(6) << Simulator::Now().GetSeconds()
130  << " tx " << p->GetSize() << std::endl;
131 }
132 
133 static void
134 RxTracer(Ptr<const Packet> p, Ptr<Ipv4> ipv4, uint32_t interface)
135 {
136  packetTraceStream << std::fixed << std::setprecision(6) << Simulator::Now().GetSeconds()
137  << " rx " << p->GetSize() << std::endl;
138 }
139 
140 void
142 {
143  Config::ConnectWithoutContext("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
145  Config::ConnectWithoutContext("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/PacingRate",
147  Config::ConnectWithoutContext("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold",
149  Config::ConnectWithoutContext("/NodeList/0/$ns3::Ipv4L3Protocol/Tx", MakeCallback(&TxTracer));
150  Config::ConnectWithoutContext("/NodeList/0/$ns3::Ipv4L3Protocol/Rx", MakeCallback(&RxTracer));
151 }
152 
153 int
154 main(int argc, char* argv[])
155 {
156  bool tracing = false;
157 
158  uint32_t maxBytes = 0; // value of zero corresponds to unlimited send
159  std::string transportProtocol = "ns3::TcpCubic";
160 
161  Time simulationEndTime = Seconds(5);
162  DataRate bottleneckBandwidth("10Mbps"); // value of x as shown in the above network topology
163  Time bottleneckDelay = MilliSeconds(40);
164  DataRate regLinkBandwidth(4 * bottleneckBandwidth.GetBitRate());
165  Time regLinkDelay = MilliSeconds(5);
166  DataRate maxPacingRate("4Gbps");
167 
168  bool isPacingEnabled = true;
169  bool useEcn = true;
170  bool useQueueDisc = true;
171  bool shouldPaceInitialWindow = true;
172 
173  // Configure defaults that are not based on explicit command-line arguments
174  // They may be overridden by general attribute configuration of command line
175  Config::SetDefault("ns3::TcpL4Protocol::SocketType",
176  TypeIdValue(TypeId::LookupByName(transportProtocol)));
177  Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
178 
179  CommandLine cmd(__FILE__);
180  cmd.AddValue("tracing", "Flag to enable/disable Ascii and Pcap tracing", tracing);
181  cmd.AddValue("maxBytes", "Total number of bytes for application to send", maxBytes);
182  cmd.AddValue("isPacingEnabled", "Flag to enable/disable pacing in TCP", isPacingEnabled);
183  cmd.AddValue("maxPacingRate", "Max Pacing Rate", maxPacingRate);
184  cmd.AddValue("useEcn", "Flag to enable/disable ECN", useEcn);
185  cmd.AddValue("useQueueDisc", "Flag to enable/disable queue disc on bottleneck", useQueueDisc);
186  cmd.AddValue("shouldPaceInitialWindow",
187  "Flag to enable/disable pacing of TCP initial window",
188  shouldPaceInitialWindow);
189  cmd.AddValue("simulationEndTime", "Simulation end time", simulationEndTime);
190  cmd.Parse(argc, argv);
191 
192  // Configure defaults based on command-line arguments
193  Config::SetDefault("ns3::TcpSocketState::EnablePacing", BooleanValue(isPacingEnabled));
194  Config::SetDefault("ns3::TcpSocketState::PaceInitialWindow",
195  BooleanValue(shouldPaceInitialWindow));
196  Config::SetDefault("ns3::TcpSocketBase::UseEcn",
198  Config::SetDefault("ns3::TcpSocketState::MaxPacingRate", DataRateValue(maxPacingRate));
199 
200  NS_LOG_INFO("Create nodes.");
201  NodeContainer c;
202  c.Create(6);
203 
204  NS_LOG_INFO("Create channels.");
205  NodeContainer n0n2 = NodeContainer(c.Get(0), c.Get(2));
206  NodeContainer n1n2 = NodeContainer(c.Get(1), c.Get(2));
207 
208  NodeContainer n2n3 = NodeContainer(c.Get(2), c.Get(3));
209 
210  NodeContainer n3n4 = NodeContainer(c.Get(3), c.Get(4));
211  NodeContainer n3n5 = NodeContainer(c.Get(3), c.Get(5));
212 
213  // Define Node link properties
214  PointToPointHelper regLink;
215  regLink.SetDeviceAttribute("DataRate", DataRateValue(regLinkBandwidth));
216  regLink.SetChannelAttribute("Delay", TimeValue(regLinkDelay));
217 
218  NetDeviceContainer d0d2 = regLink.Install(n0n2);
219  NetDeviceContainer d1d2 = regLink.Install(n1n2);
220  NetDeviceContainer d3d4 = regLink.Install(n3n4);
221  NetDeviceContainer d3d5 = regLink.Install(n3n5);
222 
223  PointToPointHelper bottleNeckLink;
224  bottleNeckLink.SetDeviceAttribute("DataRate", DataRateValue(bottleneckBandwidth));
225  bottleNeckLink.SetChannelAttribute("Delay", TimeValue(bottleneckDelay));
226 
227  NetDeviceContainer d2d3 = bottleNeckLink.Install(n2n3);
228 
229  // Install Internet stack
231  stack.Install(c);
232 
233  // Install traffic control
234  if (useQueueDisc)
235  {
236  TrafficControlHelper tchBottleneck;
237  tchBottleneck.SetRootQueueDisc("ns3::FqCoDelQueueDisc");
238  tchBottleneck.Install(d2d3);
239  }
240 
241  NS_LOG_INFO("Assign IP Addresses.");
243  ipv4.SetBase("10.1.1.0", "255.255.255.0");
244  Ipv4InterfaceContainer regLinkInterface0 = ipv4.Assign(d0d2);
245 
246  ipv4.SetBase("10.1.2.0", "255.255.255.0");
247  Ipv4InterfaceContainer regLinkInterface1 = ipv4.Assign(d1d2);
248 
249  ipv4.SetBase("10.1.3.0", "255.255.255.0");
250  Ipv4InterfaceContainer bottleneckInterface = ipv4.Assign(d2d3);
251 
252  ipv4.SetBase("10.1.4.0", "255.255.255.0");
253  Ipv4InterfaceContainer regLinkInterface4 = ipv4.Assign(d3d4);
254 
255  ipv4.SetBase("10.1.5.0", "255.255.255.0");
256  Ipv4InterfaceContainer regLinkInterface5 = ipv4.Assign(d3d5);
257 
259 
260  NS_LOG_INFO("Create Applications.");
261 
262  // Two Sink Applications at n4 and n5
263  uint16_t sinkPort = 8080;
264  Address sinkAddress4(
265  InetSocketAddress(regLinkInterface4.GetAddress(1), sinkPort)); // interface of n4
266  Address sinkAddress5(
267  InetSocketAddress(regLinkInterface5.GetAddress(1), sinkPort)); // interface of n5
268  PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory",
270  ApplicationContainer sinkApps4 = packetSinkHelper.Install(c.Get(4)); // n4 as sink
271  ApplicationContainer sinkApps5 = packetSinkHelper.Install(c.Get(5)); // n5 as sink
272 
273  sinkApps4.Start(Seconds(0));
274  sinkApps4.Stop(simulationEndTime);
275  sinkApps5.Start(Seconds(0));
276  sinkApps5.Stop(simulationEndTime);
277 
278  // Randomize the start time between 0 and 1ms
279  Ptr<UniformRandomVariable> uniformRv = CreateObject<UniformRandomVariable>();
280  uniformRv->SetStream(0);
281 
282  // Two Source Applications at n0 and n1
283  BulkSendHelper source0("ns3::TcpSocketFactory", sinkAddress4);
284  BulkSendHelper source1("ns3::TcpSocketFactory", sinkAddress5);
285  // Set the amount of data to send in bytes. Zero is unlimited.
286  source0.SetAttribute("MaxBytes", UintegerValue(maxBytes));
287  source1.SetAttribute("MaxBytes", UintegerValue(maxBytes));
288  ApplicationContainer sourceApps0 = source0.Install(c.Get(0));
289  ApplicationContainer sourceApps1 = source1.Install(c.Get(1));
290 
291  sourceApps0.Start(MicroSeconds(uniformRv->GetInteger(0, 1000)));
292  sourceApps0.Stop(simulationEndTime);
293  sourceApps1.Start(MicroSeconds(uniformRv->GetInteger(0, 1000)));
294  sourceApps1.Stop(simulationEndTime);
295 
296  if (tracing)
297  {
298  AsciiTraceHelper ascii;
299  regLink.EnableAsciiAll(ascii.CreateFileStream("tcp-dynamic-pacing.tr"));
300  regLink.EnablePcapAll("tcp-dynamic-pacing", false);
301  }
302 
303  cwndStream.open("tcp-dynamic-pacing-cwnd.dat", std::ios::out);
304  cwndStream << "#Time(s) Congestion Window (B)" << std::endl;
305 
306  pacingRateStream.open("tcp-dynamic-pacing-pacing-rate.dat", std::ios::out);
307  pacingRateStream << "#Time(s) Pacing Rate (Mb/s)" << std::endl;
308 
309  ssThreshStream.open("tcp-dynamic-pacing-ssthresh.dat", std::ios::out);
310  ssThreshStream << "#Time(s) Slow Start threshold (B)" << std::endl;
311 
312  packetTraceStream.open("tcp-dynamic-pacing-packet-trace.dat", std::ios::out);
313  packetTraceStream << "#Time(s) tx/rx size (B)" << std::endl;
314 
316 
317  FlowMonitorHelper flowmon;
318  Ptr<FlowMonitor> monitor = flowmon.InstallAll();
319 
320  NS_LOG_INFO("Run Simulation.");
321  Simulator::Stop(simulationEndTime);
322  Simulator::Run();
323 
324  monitor->CheckForLostPackets();
325  Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier>(flowmon.GetClassifier());
326  FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats();
327  for (auto i = stats.begin(); i != stats.end(); ++i)
328  {
329  Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(i->first);
330 
331  std::cout << "Flow " << i->first << " (" << t.sourceAddress << " -> "
332  << t.destinationAddress << ")\n";
333  std::cout << " Tx Packets: " << i->second.txPackets << "\n";
334  std::cout << " Tx Bytes: " << i->second.txBytes << "\n";
335  std::cout << " TxOffered: "
336  << i->second.txBytes * 8.0 / simulationEndTime.GetSeconds() / 1000 / 1000
337  << " Mbps\n";
338  std::cout << " Rx Packets: " << i->second.rxPackets << "\n";
339  std::cout << " Rx Bytes: " << i->second.rxBytes << "\n";
340  std::cout << " Throughput: "
341  << i->second.rxBytes * 8.0 / simulationEndTime.GetSeconds() / 1000 / 1000
342  << " Mbps\n";
343  }
344 
345  cwndStream.close();
346  pacingRateStream.close();
347  ssThreshStream.close();
349 
350  return 0;
351 }
NodeContainer n2n3
Nodecontainer n2 + n3.
NodeContainer n1n2
Nodecontainer n1 + n2.
NodeContainer n3n4
Nodecontainer n3 + n4.
NodeContainer n0n2
Nodecontainer n0 + n2.
NodeContainer n3n5
Nodecontainer n3 + n5.
a polymophic address class
Definition: address.h:101
holds a vector of ns3::Application pointers.
void Start(Time start) const
Start all of the Applications in this container at the start time given as a parameter.
void Stop(Time stop) const
Arrange for all of the Applications in this container to Stop() at the Time given as a parameter.
void EnableAsciiAll(std::string prefix)
Enable ascii trace output on each device (which is of the appropriate type) in the set of all nodes c...
Manage ASCII trace files for device models.
Definition: trace-helper.h:174
Ptr< OutputStreamWrapper > CreateFileStream(std::string filename, std::ios::openmode filemode=std::ios::out)
Create and initialize an output stream object we'll use to write the traced bits.
A helper to make it easier to instantiate an ns3::BulkSendApplication on a set of nodes.
Parse command-line arguments.
Definition: command-line.h:232
Class for representing data rates.
Definition: data-rate.h:89
uint64_t GetBitRate() const
Get the underlying bitrate.
Definition: data-rate.cc:305
Hold variables of type enum.
Definition: enum.h:62
Helper to enable IP flow monitoring on a set of Nodes.
Ptr< FlowClassifier > GetClassifier()
Retrieve the FlowClassifier object for IPv4 created by the Install* methods.
Ptr< FlowMonitor > InstallAll()
Enable flow monitoring on all nodes.
std::map< FlowId, FlowStats > FlowStatsContainer
Container: FlowId, FlowStats.
Definition: flow-monitor.h:231
an Inet address class
aggregate IP/TCP/UDP functionality to existing Nodes.
A helper class to make life easier while doing simple IPv4 address assignment in scripts.
static Ipv4Address GetAny()
static void PopulateRoutingTables()
Build a routing database and initialize the routing tables of the nodes in the simulation.
holds a vector of std::pair of Ptr<Ipv4> and interface index.
Ipv4Address GetAddress(uint32_t i, uint32_t j=0) const
holds a vector of ns3::NetDevice pointers
keep track of a set of node pointers.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
uint32_t GetSize() const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:861
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
void EnablePcapAll(std::string prefix, bool promiscuous=false)
Enable pcap output on each device (which is of the appropriate type) in the set of all nodes created ...
Build a set of PointToPointNetDevice objects.
void SetDeviceAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each NetDevice created by the helper.
void SetChannelAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each Channel created by the helper.
NetDeviceContainer Install(NodeContainer c)
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:571
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:142
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
static void Run()
Run the simulation.
Definition: simulator.cc:178
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:186
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:403
Build a set of QueueDisc objects.
QueueDiscContainer Install(NetDeviceContainer c)
uint16_t SetRootQueueDisc(const std::string &type, Args &&... args)
Helper function used to set a root queue disc of the given type and with the given attributes.
static TypeId LookupByName(std::string name)
Get a TypeId by name.
Definition: type-id.cc:835
Hold an unsigned integer type.
Definition: uinteger.h:45
uint32_t GetInteger(uint32_t min, uint32_t max)
Get the next random value drawn from the distribution.
void SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc:890
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition: config.cc:950
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1350
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
stack
Definition: first.py:44
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:704
cmd
Definition: second.py:40
Structure to classify a packet.
Ipv4Address sourceAddress
Source address.
Ipv4Address destinationAddress
Destination address.
std::ofstream packetTraceStream
Definition: tcp-pacing.cc:103
static void TxTracer(Ptr< const Packet > p, Ptr< Ipv4 > ipv4, uint32_t interface)
Definition: tcp-pacing.cc:127
static void RxTracer(Ptr< const Packet > p, Ptr< Ipv4 > ipv4, uint32_t interface)
Definition: tcp-pacing.cc:134
std::ofstream ssThreshStream
Definition: tcp-pacing.cc:102
static void CwndTracer(uint32_t oldval, uint32_t newval)
Definition: tcp-pacing.cc:106
std::ofstream pacingRateStream
Definition: tcp-pacing.cc:101
std::ofstream cwndStream
Definition: tcp-pacing.cc:98
static void SsThreshTracer(uint32_t oldval, uint32_t newval)
Definition: tcp-pacing.cc:120
static void PacingRateTracer(DataRate oldval, DataRate newval)
Definition: tcp-pacing.cc:113
void ConnectSocketTraces()
Definition: tcp-pacing.cc:141
bool tracing
Flag to enable/disable generation of tracing files.