|
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ |
|
/* |
|
* Copyright (c) 2009 The Boeing Company |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation; |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
* |
|
*/ |
|
|
|
// |
|
// ./waf --run="wired-tcp --totalNodes=10" |
|
// |
|
// topo: more than one hop can have 1 bottleneck if enabled |
|
// |
|
|
|
#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/flow-monitor-helper.h" |
|
#include "ns3/ipv4-flow-classifier.h" |
|
#include "ns3/traffic-control-module.h" |
|
#include <fstream> |
|
#include <vector> |
|
#include <string> |
|
#include <iomanip> |
|
#include <map> |
|
|
|
using namespace ns3; |
|
|
|
NS_LOG_COMPONENT_DEFINE ("wired-tcp"); |
|
|
|
Ptr<PacketSink> sink; /* Pointer to the packet sink application */ |
|
|
|
typedef struct D { |
|
uint32_t rxPkts; |
|
uint32_t txPkts; |
|
double throughput; |
|
} Data; |
|
|
|
std::map<uint32_t, Data> data; |
|
|
|
void SimRun (uint32_t payloadSize, uint64_t maxBytes, uint32_t totalNodes, |
|
bool enableBtl, bool enableSack, bool enableEcn, double errRate, |
|
double simTime, std::string tcpVariant, uint32_t runID, |
|
uint32_t baseQ, uint32_t codelQ, uint32_t tcpQ) |
|
{ |
|
if( baseQ > 0 ) { |
|
Config::SetDefault("ns3::QueueBase::MaxSize", StringValue(std::to_string(baseQ)+"p")); |
|
} |
|
|
|
std::string tcpAlg = tcpVariant; |
|
tcpVariant = std::string ("ns3::") + tcpVariant; |
|
// Select TCP variant |
|
if (tcpVariant.compare ("ns3::TcpWestwoodPlus") == 0) |
|
{ |
|
// TcpWestwoodPlus is not an actual TypeId name; we need TcpWestwood here |
|
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpWestwood::GetTypeId ())); |
|
// the default protocol type in ns3::TcpWestwood is WESTWOOD |
|
Config::SetDefault ("ns3::TcpWestwood::ProtocolType", EnumValue (TcpWestwood::WESTWOODPLUS)); |
|
} |
|
else |
|
{ |
|
TypeId tcpTid; |
|
NS_ABORT_MSG_UNLESS (TypeId::LookupByNameFailSafe (tcpVariant, &tcpTid), "TypeId " << tcpVariant << " not found"); |
|
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TypeId::LookupByName (tcpVariant))); |
|
} |
|
|
|
/* Configure TCP Options */ |
|
Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (payloadSize)); |
|
Config::SetDefault ("ns3::TcpSocketBase::Sack", BooleanValue (enableSack)); |
|
|
|
if (tcpQ > 0) { |
|
Config::SetDefault ("ns3::TcpSocket::SndBufSize", UintegerValue (tcpQ*1500)); // 1500 MSS |
|
Config::SetDefault ("ns3::TcpSocket::RcvBufSize", UintegerValue (tcpQ*1500)); |
|
} |
|
|
|
/* Enable or disable Congestion Window Limit */ |
|
// Config::SetDefault ("ns3::TcpSocketState::EnableCWL", BooleanValue (enableCWL)); |
|
|
|
TrafficControlHelper tchCoDel; |
|
tchCoDel.SetRootQueueDisc ("ns3::CoDelQueueDisc"); |
|
|
|
if (enableEcn) { |
|
Config::SetDefault ("ns3::CoDelQueueDisc::UseEcn", BooleanValue (true)); |
|
Config::SetDefault ("ns3::TcpSocketBase::EcnMode", StringValue ("ClassicEcn")); |
|
} |
|
|
|
if (codelQ > 0) { |
|
Config::SetDefault ("ns3::CoDelQueueDisc::MaxSize", StringValue (std::to_string(codelQ)+"p")); |
|
} |
|
|
|
NodeContainer c; |
|
c.Create (totalNodes); |
|
InternetStackHelper stack; |
|
stack.Install (c); |
|
|
|
// totalNodes-1 links and subnets |
|
uint32_t midNode = (totalNodes/2.0); |
|
PointToPointHelper p2p; |
|
std::vector<NetDeviceContainer> devices(totalNodes-1); |
|
for (uint32_t i = 0; i < totalNodes-1; i++) { |
|
if ( i > 0 && i == midNode && enableBtl) { |
|
p2p.SetDeviceAttribute ("DataRate", StringValue ("1Mbps")); |
|
p2p.SetChannelAttribute ("Delay", StringValue ("10ms")); |
|
} |
|
else { |
|
p2p.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); |
|
p2p.SetChannelAttribute ("Delay", StringValue ("1ms")); |
|
} |
|
devices[i] = p2p.Install(c.Get(i), c.Get(i+1)); |
|
tchCoDel.Install (devices[i]); |
|
} |
|
|
|
NS_LOG_INFO ("assigning ip address"); |
|
|
|
Ipv4AddressHelper ipv4; |
|
NS_LOG_INFO ("Assign IP Addresses."); |
|
std::vector<Ipv4InterfaceContainer> iface (totalNodes-1); |
|
|
|
// set the n-1 subnets |
|
for (uint32_t i = 0; i < totalNodes-1; i++) { |
|
std::string subnetIP= "10.1." + std::to_string(i+1) + ".0"; |
|
ipv4.SetBase (subnetIP.c_str(), "255.255.255.0"); |
|
iface[i] = ipv4.Assign (devices[i]); |
|
} |
|
|
|
// Create router nodes, initialize routing database and set up the routing |
|
// tables in the nodes. |
|
Ipv4GlobalRoutingHelper::PopulateRoutingTables (); |
|
|
|
DoubleValue rate (errRate); |
|
Ptr<RateErrorModel> em1 = |
|
CreateObjectWithAttributes<RateErrorModel> ("RanVar", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=1.0]"), "ErrorRate", rate); |
|
Ptr<RateErrorModel> em2 = |
|
CreateObjectWithAttributes<RateErrorModel> ("RanVar", StringValue ("ns3::UniformRandomVariable[Min=0.0|Max=1.0]"), "ErrorRate", rate); |
|
|
|
// This enables the specified errRate on both link endpoints. |
|
devices[0].Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (em1)); |
|
devices[totalNodes-2].Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em2)); |
|
|
|
NS_LOG_INFO ("Create Applications."); |
|
|
|
// |
|
// Create a BulkSendApplication and install it on node 0 |
|
// |
|
|
|
uint16_t port = 9; // well-known echo port number |
|
BulkSendHelper source ("ns3::TcpSocketFactory", |
|
InetSocketAddress (iface[totalNodes-2].GetAddress(1), port)); |
|
// Set the amount of data to send in bytes. Zero is unlimited. |
|
source.SetAttribute ("MaxBytes", UintegerValue (maxBytes)); |
|
ApplicationContainer sourceApps = source.Install (c.Get (0)); |
|
sourceApps.Start (Seconds (0.0)); |
|
sourceApps.Stop (Seconds (simTime)); |
|
|
|
// |
|
// Create a PacketSinkApplication and install it on node 1 |
|
// |
|
PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", |
|
InetSocketAddress (Ipv4Address::GetAny (), port)); |
|
ApplicationContainer sinkApps = sinkHelper.Install (c.Get (totalNodes-1)); |
|
sink = StaticCast<PacketSink> (sinkApps.Get (0)); |
|
sinkApps.Start (Seconds (0.0)); |
|
sinkApps.Stop (Seconds (simTime)); |
|
|
|
FlowMonitorHelper flowmon; |
|
Ptr<FlowMonitor> monitor = flowmon.InstallAll (); |
|
|
|
Simulator::Stop (Seconds (simTime+2.0)); |
|
|
|
Simulator::Run (); |
|
|
|
double throughput = ((sink->GetTotalRx () * 8) / (1e6 * simTime)); |
|
uint64_t totalRxPkts = sink->GetTotalRx()/payloadSize; |
|
uint32_t totalTxPkts = 0; |
|
|
|
// 10. Print per flow statistics |
|
monitor->CheckForLostPackets (); |
|
Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmon.GetClassifier ()); |
|
FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats (); |
|
for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i = stats.begin (); i != stats.end (); ++i) |
|
{ |
|
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow (i->first); |
|
if (t.sourceAddress == iface[0].GetAddress (0) && |
|
t.destinationAddress == iface[totalNodes-2].GetAddress (1) ) |
|
{ |
|
//std::cout << "Flow " << i->first << " (" << t.sourceAddress << " -> " << t.destinationAddress << ")\n"; |
|
totalTxPkts = i->second.txPackets; |
|
break; |
|
} |
|
} |
|
|
|
if ( data.find(totalNodes) == data.end() ) { |
|
data[totalNodes].rxPkts = 0; |
|
data[totalNodes].txPkts = 0; |
|
data[totalNodes].throughput = 0.0; |
|
} |
|
|
|
data[totalNodes].rxPkts += totalRxPkts; |
|
data[totalNodes].txPkts += totalTxPkts; |
|
data[totalNodes].throughput += throughput; |
|
|
|
std::ofstream cwndWriter; |
|
|
|
std::string str = "logs/linear/tcp-cwnd/n-" + |
|
std::to_string(totalNodes) + |
|
"-" + tcpAlg + |
|
"-btl-" + std::to_string(enableBtl) + |
|
"-bq-" + std::to_string(baseQ) + |
|
"-cq-" + std::to_string(codelQ) + |
|
"-tq-" + std::to_string(tcpQ) + |
|
"-sack-" + std::to_string(enableSack) + |
|
"-ecn-" + std::to_string(enableEcn) + |
|
"-err-" + std::to_string(errRate) + |
|
"-run-" + std::to_string(runID) + |
|
".txt"; |
|
|
|
auto serverApp = DynamicCast<ns3::BulkSendApplication>(sourceApps.Get(0)); |
|
cwnd = serverApp->GetCwndTrace(); |
|
cwndWriter.open (str.c_str()); |
|
|
|
for (uint32_t i = 0; i < cwnd.size(); i++) |
|
{ |
|
cwndWriter << (cwnd[i].first-0.0)/1000000000.0 << "\t" |
|
<< (double)(cwnd[i].second/payloadSize) << "\n"; |
|
} |
|
cwndWriter.close(); |
|
|
|
Simulator::Destroy (); |
|
} |
|
|
|
int main (int argc, char *argv[]) |
|
{ |
|
uint32_t payloadSize = 1460; // bytes |
|
uint64_t maxBytes = std::numeric_limits<uint64_t>::max()-10; // 10*1024*1024; // 10MB=10*1024*1024 |
|
double simTime = 10.0; |
|
std::string tcpVariant = "TcpNewReno"; /* TCP variant type. */ |
|
uint32_t totalNodes = 2; |
|
uint32_t numRun = 1; |
|
bool enableCWL = true; |
|
bool enableSack = true; |
|
bool enableEcn = true; |
|
bool enableBtl = true; |
|
double errRate = 0.000001; // 1460 ~ 1% |
|
uint32_t baseQ = 0; // ns3 default = 100 packets |
|
uint32_t codelQ = 0; // ns3 default = 1000 packets |
|
uint32_t tcpQ = 0; // keep 0 for default; ns3 Tx/Rx buffer default initial = 131072 bytes |
|
|
|
CommandLine cmd; |
|
cmd.AddValue ("payloadSize", "size of application payload sent", payloadSize); |
|
cmd.AddValue ("maxBytes","Total number of bytes for application to send", maxBytes); |
|
cmd.AddValue ("totalNodes","Total number of nodes in the chain", totalNodes); |
|
cmd.AddValue ("tcpVariant","TCP congestion control algorithm", tcpVariant); |
|
cmd.AddValue ("enableCWL","Enable or Disable Congestion Window Limit", enableCWL); |
|
cmd.AddValue ("enableSack","Enable or Disable TCP SACK", enableSack); |
|
cmd.AddValue ("enableEcn","Enable or Disable Explicit Congestion Notification", enableEcn); |
|
cmd.AddValue ("enableBtl","Enable or Disable bottleneck link", enableBtl); |
|
cmd.AddValue ("errRate", "Error rate to apply to link", errRate); |
|
cmd.AddValue ("simTime","Set simulation time limit", simTime); |
|
cmd.AddValue ("baseQ","Set base queue size", baseQ); |
|
cmd.AddValue ("codelQ","Set codel queue size", codelQ); |
|
cmd.AddValue ("tcpQ","Set tcp buffer size", tcpQ); |
|
cmd.AddValue ("numRun","Set simulation runs", numRun); |
|
cmd.Parse (argc, argv); |
|
|
|
for (uint32_t i = 1; i <= numRun; i+=1) |
|
{ |
|
RngSeedManager::SetSeed (i); // Changes seed from default of 1 to 3 |
|
// RngSeedManager::SetRun (7); // Changes run number from default of 1 to 7 |
|
SimRun(payloadSize, maxBytes, totalNodes, enableBtl, |
|
enableSack, enableEcn, errRate, simTime, tcpVariant, i, |
|
baseQ, codelQ, tcpQ); |
|
} |
|
|
|
std::ofstream throughputWriter; |
|
std::string str = "logs/linear/throughput/wired-tcp-n-" + |
|
std::to_string(totalNodes) + |
|
"-" + tcpVariant + |
|
"-btl-" + std::to_string(enableBtl) + |
|
"-bq-" + std::to_string(baseQ) + |
|
"-cq-" + std::to_string(codelQ) + |
|
"-tq-" + std::to_string(tcpQ) + |
|
"-sack-" + std::to_string(enableSack) + |
|
"-ecn-" + std::to_string(enableEcn) + |
|
"-err-" + std::to_string(errRate) + |
|
".txt"; |
|
|
|
throughputWriter.open(str.c_str(), std::ios::app); |
|
|
|
if (throughputWriter.is_open()) { |
|
throughputWriter << totalNodes << "\t" |
|
<< std::fixed << std::setprecision(2) |
|
<< data[totalNodes].throughput/numRun << "\t" |
|
<< (double)(data[totalNodes].rxPkts/numRun) << "\t" |
|
<< (double)(data[totalNodes].txPkts/numRun) << "\t" |
|
<< std::endl; |
|
} |
|
else { |
|
std::cerr << "Failed file open\n"; |
|
} |
|
|
|
throughputWriter.close(); |
|
|
|
return 0; |
|
} |