commit 290ca257ac6213a36182e2bc562a7f6a7f4efd13 Author: josch Date: Mon Nov 11 12:14:45 2013 +0100 initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..719f7f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: dumbbell-sim + +dumbbell-sim: dumbbell-sim.cc + g++ -I/usr/include/ns3.17 -lns3.17-applications -lns3.17-internet -lns3.17-network -lns3.17-core -lns3.17-point-to-point -Wall dumbbell-sim.cc -o dumbbell-sim diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c5c044 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +Simulation of a dumbbell topology using ns3 3.17 +------------------------------------------------ + +dependencies (on Debian based distros): + + libns3-dev (= 3.17) + +compilation: + + make + +example execution: + + $ cat << END | ./dumbbell-sim > log + UR TCP 536 0.0 1250 + UR TCP 536 250 500 + LR UDP 1000 750 1000 0.9Mbps + END + +The file `log` then contains traces with the first column being the timestamp, +the second column being the ns3 context and the third column being the traced +value of this context. The traced values are TCP congestion window size +changes, received packets and queue drops. + +The traffic description is done via standard input. Each line represents one +flow and the dumbbell will be created such that it has as many clients on each +side as there are flows. Each flow will originate from a unique node N on the +left side of the dumbbell and reach node N on the right side of the dumbbell. +This way, every node is involved in exactly one flow and the connection in the +center of the dumbbell serves as the bottleneck. + +So above example would create the following topology: + + L1 R1 + \ / + \+--+ +--+/ + L2---|LR|----|RR|---R2 + /+--+ +--|\ + / \ + L3 R3 + +The flow given in the first line would originate from `L1`, go through the +bottleneck link between the left and right router (`LR` and `RR`, respectively) +and end at `R1`. The flow given in the second line would go from `L2` to `R2` +and the last one from `L3` to `R3`. + +Flow description format +----------------------- + +Each line represents one flow between two unique nodes of the dumbbell, flowing +from left to right. Each line is split by their white spaces. The first element +represents the type of flow. There are three flow types. + +**LR**: this type stands for "limited rate" and allows to specify traffic which +is limited by transmission rate. Start and stop times govern the duration of +this traffic. + +**UR**: this type stands for "unlimited rate" and allows to specify traffic +which is sent as fast as possible. Only TCP traffic is allowed. Start and stop +times govern the duration of this traffic. + +**LT**: this type stands for "limited transfer" and allows to specify traffic +which is sent as fast as possible. Only TCP traffic is allowed. The amount of +data to transfer governs when this flow stops transmitting. + +The second element in each line is the transport type an can be either "TCP" or +"UDP". Observe the restrictions of transport type with respect to the flow +type. + +The third element is the package size to send. For TCP traffic this will also +adapt the segment size of the underlying socket. + +The fourth element is the start time of the flow given in seconds. + +For `LR` and `UR` flow types, the fifth element is the stop time given in +seconds. For the `LT` type, the fifth element is the maximum transfer size, +given in bytes. For all types, the fifth element governs when the flow will +stop. + +The `LR` type takes a sixth element, specifying the desired transmission rate. +This value is given with a unit like "Mbps". + +Commandline Arguments +--------------------- + +While flow descriptions are read from standard input, commandline arguments +allow to setup properties of the dumbbell topology. By default, the error rate +of the involved links is 0.0, which means no packet is dropped by the physical +links. The default TCP congestion-avoidance algorithm is NewReno and other +options are Tahoe, Reno, Westwood and WestwoodPlus. The default bandwith and +latency of the bottleneck link is 1Mbps and 50ms, respectively. The default +bandwidth and latency of the access links of the nodes to the routers is 10Mbps +and 1ms, respectively. + +When giving an error rate other than 0.0, a random element is introduced. It is +possible to make this randomness deterministic by using the `--run` option +which allows to make deterministic runs with the same outcome, given the same +run number. + +Depending on the flow definitions you might want to start the simulation early. +This is possible with the `--simstop` argument. + +You can enable pcap tracing of all links with the `--tracing` option. diff --git a/dumbbell-sim.cc b/dumbbell-sim.cc new file mode 100644 index 0000000..4a82b7b --- /dev/null +++ b/dumbbell-sim.cc @@ -0,0 +1,833 @@ +/* + * Copyright (C) 2013 - Johannes Schauer + * + * 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 + * + * + * This code heavily borrows from ns3 itself which are copyright of their + * respective authors and redistributable under the same conditions. + * + */ + +#include // for size_t +#include // for uint32_t +#include // for exit +#include // for min +#include // for operator<<, setw +#include // for operator<<, basic_ostream, etc +#include // for string, allocator, etc +#include // for vector +#include "ns3/address.h" // for Address +#include "ns3/application-container.h" // for ApplicationContainer +#include "ns3/application.h" // for Application +#include "ns3/boolean.h" // for BooleanValue +#include "ns3/callback.h" // for MakeCallback +#include "ns3/command-line.h" // for CommandLine +#include "ns3/config.h" // for SetDefault, Connect +#include "ns3/data-rate.h" // for DataRate +#include "ns3/double.h" // for DoubleValue +#include "ns3/enum.h" // for EnumValue +#include "ns3/error-model.h" // for RateErrorModel +#include "ns3/event-id.h" // for EventId +#include "ns3/fatal-error.h" // for NS_FATAL_ERROR +#include "ns3/global-value.h" // for GlobalValue +#include "ns3/header.h" // for Header +#include "ns3/inet-socket-address.h" // for InetSocketAddress +#include "ns3/internet-stack-helper.h" // for InternetStackHelper +#include "ns3/ipv4-address-helper.h" // for Ipv4AddressHelper +#include "ns3/ipv4-address.h" // for operator<<, Ipv4Address, etc +#include "ns3/ipv4-global-routing-helper.h" +#include "ns3/ipv4-header.h" // for Ipv4Header +#include "ns3/ipv4-interface-container.h" // for Ipv4InterfaceContainer +#include "ns3/ipv4.h" // for Ipv4 +#include "ns3/net-device-container.h" // for NetDeviceContainer +#include "ns3/net-device.h" // for NetDevice +#include "ns3/node-container.h" // for NodeContainer +#include "ns3/node.h" // for Node +#include "ns3/nstime.h" // for Seconds, Time +#include "ns3/object.h" // for CreateObject +#include "ns3/packet-sink-helper.h" // for PacketSinkHelper +#include "ns3/packet.h" // for Packet +#include "ns3/point-to-point-helper.h" // for PointToPointHelper +#include "ns3/pointer.h" // for PointerValue +#include "ns3/ptr.h" // for Ptr, Create, DynamicCast +#include "ns3/rng-seed-manager.h" // for SeedManager +#include "ns3/simulator.h" // for Simulator +#include "ns3/socket.h" // for Socket, etc +#include "ns3/string.h" // for StringValue +#include "ns3/tcp-header.h" // for TcpHeader +#include "ns3/tcp-newreno.h" // for TcpNewReno +#include "ns3/tcp-reno.h" // for TcpReno +#include "ns3/tcp-socket-factory.h" // for TcpSocketFactory +#include "ns3/tcp-socket.h" // for TcpSocket +#include "ns3/tcp-tahoe.h" // for TcpTahoe +#include "ns3/tcp-westwood.h" // for TcpWestwood, etc +#include "ns3/type-id.h" // for TypeIdValue +#include "ns3/udp-header.h" // for UdpHeader +#include "ns3/udp-socket-factory.h" // for UdpSocketFactory +#include "ns3/uinteger.h" // for UintegerValue + +/* + * we need to roll our own app instead of using one of the supplied helpers + * because of the following reasons: + * + * - bulktransferhelper can't do udp + * - onoffhelper doesnt allow access to the underlying sockets + * - not having access to the underlying sockets, means that any tracer can + * only be registered *after* the app created by the helper is started. + * This is ugly as it forces to start multiple tracers manually in synch + * with the app start time. Since it has to be multiple tracers, one cannot + * anymore use the wildcard mechanism to have a one-tracer-catches-all + * setup. + * + * this app allows to send an infinite number of tcp or udp packets at a fixed + * rate + * */ +class LimitedRateApp : public ns3::Application +{ + public: + + LimitedRateApp(); + virtual ~ LimitedRateApp(); + + void Setup(ns3::Ptr socket, ns3::Address address, + uint32_t packetSize, ns3::DataRate dataRate); + + private: + virtual void StartApplication(void); + virtual void StopApplication(void); + + void ScheduleTx(void); + void SendPacket(void); + + ns3::Ptr m_socket; + ns3::Address m_peer; + uint32_t m_packetSize; + ns3::DataRate m_dataRate; + ns3::EventId m_sendEvent; + bool m_running; +}; + +LimitedRateApp::LimitedRateApp() + : m_socket(0), + m_peer(), + m_packetSize(0), + m_dataRate(0), + m_sendEvent(), + m_running(false) +{ +} + +LimitedRateApp::~LimitedRateApp() +{ + m_socket = 0; +} + + void +LimitedRateApp::Setup(ns3::Ptr socket, + ns3::Address address, uint32_t packetSize, + ns3::DataRate dataRate) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_dataRate = dataRate; +} + +void LimitedRateApp::StartApplication(void) +{ + m_running = true; + m_socket->Bind(); + m_socket->Connect(m_peer); + SendPacket(); +} + +void LimitedRateApp::StopApplication(void) +{ + m_running = false; + + if (m_sendEvent.IsRunning()) { + ns3::Simulator::Cancel(m_sendEvent); + } + + if (m_socket) { + m_socket->Close(); + } +} + +void LimitedRateApp::SendPacket(void) +{ + ns3::Ptr packet = + ns3::Create(m_packetSize); + m_socket->Send(packet); + ScheduleTx(); +} + +void LimitedRateApp::ScheduleTx(void) +{ + if (m_running) { + ns3::Time tNext(ns3::Seconds( + m_packetSize * 8 / + static_cast(m_dataRate.GetBitRate()))); + m_sendEvent = ns3::Simulator::Schedule( + tNext, &LimitedRateApp::SendPacket, this); + } +} + +/* + * this app borrows from the BulkSendApplication but keeps sending for an + * infinite time (until Stop() is reached) and receives an already created + * socket instead of creating its own + * + * it has unlimited rate in the sense that it pushes new packets into the + * sending buffer of the socket until it is full and keeps filling that buffer + * once the socket becomes ready to send again. + * + * due to this nature of operation it is only suitable for streaming sockets + */ +class UnlimitedRateApp : public ns3::Application +{ + public: + UnlimitedRateApp(); + virtual ~ UnlimitedRateApp(); + + void Setup(ns3::Ptr socket, ns3::Address address, + uint32_t packetSize); + + private: + // inherited from Application base class. + virtual void StartApplication(void); // Called at time specified by Start + virtual void StopApplication(void); // Called at time specified by Stop + + void SendData(); + + ns3::Ptr m_socket; // Associated socket + ns3::Address m_peer; // Peer address + bool m_connected; // True if connected + uint32_t m_packetSize; // Size of data to send each time + + void ConnectionSucceeded(ns3::Ptr socket); + void ConnectionFailed(ns3::Ptr socket); + void DataSend(ns3::Ptr, uint32_t); // for socket's SetSendCallback + void Ignore(ns3::Ptr socket); +}; + +UnlimitedRateApp::UnlimitedRateApp() + : m_socket(0), + m_connected(false), + m_packetSize(512) +{ +} + +UnlimitedRateApp::~UnlimitedRateApp() +{ + m_socket = 0; +} + + void +UnlimitedRateApp::Setup(ns3::Ptr socket, + ns3::Address address, uint32_t packetSize) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + + // Fatal error if socket type is not NS3_SOCK_STREAM or NS3_SOCK_SEQPACKET + if (m_socket->GetSocketType() != ns3::Socket::NS3_SOCK_STREAM && + m_socket->GetSocketType() != ns3::Socket::NS3_SOCK_SEQPACKET) { + NS_FATAL_ERROR + ("Using UnlimitedRateApp with an incompatible socket type. " + "UnlimitedRateApp requires SOCK_STREAM or SOCK_SEQPACKET. " + "In other words, use TCP instead of UDP."); + } +} + +void UnlimitedRateApp::StartApplication(void) +{ + m_socket->Bind(); + m_socket->Connect(m_peer); + m_socket->ShutdownRecv(); + m_socket->SetConnectCallback(ns3::MakeCallback( + &UnlimitedRateApp::ConnectionSucceeded, this), + ns3::MakeCallback( + &UnlimitedRateApp::ConnectionFailed, this)); + m_socket->SetSendCallback(ns3::MakeCallback( + &UnlimitedRateApp::DataSend, this)); + if (m_connected) + SendData(); +} + +void UnlimitedRateApp::StopApplication(void) // Called at time specified by Stop +{ + if (m_socket != 0) { + m_socket->Close(); + m_connected = false; + } +} + +void UnlimitedRateApp::SendData(void) +{ + // We exit this loop when actual packet = + ns3::Create(m_packetSize); + int actual = m_socket->Send(packet); + if ((unsigned) actual != m_packetSize) + break; + } +} + +void UnlimitedRateApp::ConnectionSucceeded(ns3::Ptr socket) +{ + m_connected = true; + SendData(); +} + +void UnlimitedRateApp::ConnectionFailed(ns3::Ptr socket) +{ +} + +void UnlimitedRateApp::DataSend(ns3::Ptr, uint32_t) +{ + if (m_connected) { // Only send new data if the connection has completed + ns3::Simulator::ScheduleNow(&UnlimitedRateApp::SendData, this); + } +} + + +/* + * this app borrows from the BulkSendApplication but instead allows a socket + * being passed to it instead of creating its own socket internally. + * + * it has unlimited rate in the sense that it pushes new packets into the + * sending buffer of the socket until it is full and keeps filling that buffer + * once the socket becomes ready to send again. + * + * due to this nature of operation it is only suitable for streaming sockets + */ +class LimitedTransferApp : public ns3::Application +{ + public: + LimitedTransferApp(); + virtual ~ LimitedTransferApp(); + + void Setup(ns3::Ptr socket, ns3::Address address, + uint32_t packetSize, uint32_t maxBytes); + + private: + // inherited from Application base class. + virtual void StartApplication(void); // Called at time specified by Start + virtual void StopApplication(void); // Called at time specified by Stop + + void SendData(); + + ns3::Ptr m_socket; // Associated socket + ns3::Address m_peer; // Peer address + bool m_connected; // True if connected + uint32_t m_packetSize; // Size of data to send each time + uint32_t m_totBytes; // Total bytes sent so far + uint32_t m_maxBytes; // Limit total number of bytes sent + + void ConnectionSucceeded(ns3::Ptr socket); + void ConnectionFailed(ns3::Ptr socket); + void DataSend(ns3::Ptr, uint32_t); // for socket's SetSendCallback + void Ignore(ns3::Ptr socket); +}; + +LimitedTransferApp::LimitedTransferApp() + : m_socket(0), + m_connected(false), + m_packetSize(512), + m_totBytes(0), + m_maxBytes(0) +{ +} + +LimitedTransferApp::~LimitedTransferApp() +{ + m_socket = 0; +} + + void +LimitedTransferApp::Setup(ns3::Ptr socket, + ns3::Address address, uint32_t packetSize, + uint32_t maxBytes) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_maxBytes = maxBytes; + + // Fatal error if socket type is not NS3_SOCK_STREAM or NS3_SOCK_SEQPACKET + if (m_socket->GetSocketType() != ns3::Socket::NS3_SOCK_STREAM && + m_socket->GetSocketType() != ns3::Socket::NS3_SOCK_SEQPACKET) { + NS_FATAL_ERROR + ("Using LimitedTransferApp with an incompatible socket type. " + "LimitedTransferApp requires SOCK_STREAM or SOCK_SEQPACKET. " + "In other words, use TCP instead of UDP."); + } +} + +void LimitedTransferApp::StartApplication(void) +{ + m_socket->Bind(); + m_socket->Connect(m_peer); + m_socket->ShutdownRecv(); + m_socket->SetConnectCallback(MakeCallback( + &LimitedTransferApp::ConnectionSucceeded, this), + MakeCallback( + &LimitedTransferApp::ConnectionFailed, this)); + m_socket->SetSendCallback( + ns3::MakeCallback(&LimitedTransferApp::DataSend, this)); + if (m_connected) + SendData(); +} + +void LimitedTransferApp::StopApplication(void) // Called at time specified by Stop +{ + if (m_socket != 0) { + m_socket->Close(); + m_connected = false; + } +} + +void LimitedTransferApp::SendData(void) +{ + while (m_totBytes 0) { + toSend = std::min(m_packetSize, m_maxBytes - m_totBytes); + } + ns3::Ptr packet = + ns3::Create(toSend); + int actual = m_socket->Send(packet); + if (actual> 0) { + m_totBytes += actual; + } + // We exit this loop when actualClose(); + m_connected = false; + } +} + +void LimitedTransferApp::ConnectionSucceeded(ns3::Ptr + socket) +{ + m_connected = true; + SendData(); +} + +void LimitedTransferApp::ConnectionFailed(ns3::Ptr socket) +{ +} + +void LimitedTransferApp::DataSend(ns3::Ptr, uint32_t) +{ + if (m_connected) { // Only send new data if the connection has completed + ns3::Simulator::ScheduleNow(&LimitedTransferApp::SendData, this); + } +} + +static void CwndTracer(std::string context, uint32_t oldval, + uint32_t newval) +{ + if (newval> 2147483648) { + std::cerr << "impossibly high cwnd value: " << newval << std::endl; + return; + } + std::cout << std::setw(8) << ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " " << newval << std::endl; +} + +static void SsThreshTracer(std::string context, uint32_t oldval, + uint32_t newval) +{ + std::cout << std::setw(8) << ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " " << newval << std::endl; +} + +static void PacketSinkRxTracer(std::string context, + ns3::Ptr p, + const ns3::Address & addr) +{ + std::cout << std::setw(8) << ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " " << p->GetSize() << std::endl; +} + +static void TxQueueDropTracer(std::string context, + ns3::Ptr p) +{ + ns3::Ipv4Header ipv4h; + uint32_t len = p->PeekHeader(ipv4h); + if (len == 0) { + std::cout << std::setw(8) << + ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " 0" << std::endl; + } else { + std::cout << std::setw(8) << + ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " " << ipv4h.GetSource() << + " " << ipv4h.GetDestination() << std::endl; + } +} + +static void PhyRxDropTracer(std::string context, + ns3::Ptr p) +{ + std::cout << std::setw(8) << ns3::Simulator::Now().GetSeconds() << " "; + std::cout << context << " 0" << std::endl; +} + +void printHeaderSizes() +{ + ns3::Header * temp_header = new ns3::Ipv4Header(); + uint32_t ip_header = temp_header->GetSerializedSize(); + std::cerr << "IP Header size is: " << ip_header << std::endl; + delete temp_header; + temp_header = new ns3::TcpHeader(); + uint32_t tcp_header = temp_header->GetSerializedSize(); + std::cerr << "TCP Header size is: " << tcp_header << std::endl; + delete temp_header; + temp_header = new ns3::UdpHeader(); + uint32_t udp_header = temp_header->GetSerializedSize(); + std::cerr << "UDP Header size is: " << udp_header << std::endl; + delete temp_header; +} + +int main(int argc, char *argv[]) +{ + std::string transport_prot = "TcpNewReno"; + double error_p = 0.0; + std::string bottleneck_bandwidth = "1Mbps"; + std::string access_bandwidth = "10Mbps"; + std::string bottleneck_delay = "50ms"; + std::string access_delay = "1ms"; + uint32_t run = 0; + double simstop = 0.0; + bool pcaptracing = false; + + ns3::CommandLine cmd; + cmd.AddValue("tcpcaa", + "Default TCP congestion-avoidance algorithm to use: " + "TcpTahoe, TcpReno, TcpNewReno, TcpWestwood, TcpWestwoodPlus ", + transport_prot); + cmd.AddValue("error_p", "Packet error rate", error_p); + cmd.AddValue("bandwidth", "Bottleneck link bandwidth", + bottleneck_bandwidth); + cmd.AddValue("access_bandwidth", "Access link bandwidth", + access_bandwidth); + cmd.AddValue("delay", "Bottleneck link delay", bottleneck_delay); + cmd.AddValue("access delay", "Access link delay", access_delay); + cmd.AddValue("run", "Run index (for setting repeatable seeds)", run); + cmd.AddValue("simstop", "Stop simulator after this many seconds", + simstop); + cmd.AddValue("tracing", "Flag to enable/disable pcap tracing", + pcaptracing); + cmd.Parse(argc, argv); + + ns3::SeedManager::SetSeed(1); + ns3::SeedManager::SetRun(run); + + if (transport_prot.compare("TcpTahoe") == 0) + ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", + ns3::TypeIdValue(ns3::TcpTahoe::GetTypeId())); + else if (transport_prot.compare("TcpReno") == 0) + ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", + ns3::TypeIdValue(ns3::TcpReno::GetTypeId())); + else if (transport_prot.compare("TcpNewReno") == 0) + ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", + ns3::TypeIdValue(ns3::TcpNewReno::GetTypeId())); + else if (transport_prot.compare("TcpWestwood") == 0) { + ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", + ns3::TypeIdValue(ns3::TcpWestwood::GetTypeId())); + ns3::Config::SetDefault("ns3::TcpWestwood::FilterType", + ns3::EnumValue(ns3::TcpWestwood::TUSTIN)); + } else if (transport_prot.compare("TcpWestwoodPlus") == 0) { + ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", + ns3::TypeIdValue(ns3::TcpWestwood::GetTypeId())); + ns3::Config::SetDefault("ns3::TcpWestwood::ProtocolType", + ns3::EnumValue(ns3::TcpWestwood::WESTWOODPLUS)); + ns3::Config::SetDefault("ns3::TcpWestwood::FilterType", + ns3::EnumValue(ns3::TcpWestwood::TUSTIN)); + } else { + std::cerr << "Invalid TCP version"; + exit(1); + } + + /* the default is to not calculate checksums but if we want pcap traces + * then we calculate them */ + if (pcaptracing) + ns3::GlobalValue::Bind("ChecksumEnabled", ns3::BooleanValue(true)); + + // read in traffic definitions from standard input + std::cerr << "Reading traffic descriptions from standard input..." + << std::endl; + std::vector inputlines; + std::string input_line; + for (std::string s; std::getline(std::cin, s);) { + inputlines.push_back(s); + } + + uint32_t number_of_clients = inputlines.size(); + std::cerr << "creating " << number_of_clients << + " data streams" << std::endl; + + ns3::PointToPointHelper bottleNeckLink; + bottleNeckLink.SetDeviceAttribute("DataRate", + ns3::StringValue(bottleneck_bandwidth)); + bottleNeckLink.SetChannelAttribute("Delay", + ns3::StringValue(bottleneck_delay)); + + ns3::PointToPointHelper pointToPointLeaf; + pointToPointLeaf.SetDeviceAttribute("DataRate", + ns3::StringValue(access_bandwidth)); + pointToPointLeaf.SetChannelAttribute("Delay", + ns3::StringValue(access_delay)); + + int port = 9000; + + /* we cannot use the PointToPointDumbbellHelper because it hides the + * NetDeviceContainer it creates from us. Therefore, we are creating the + * dumbbell topology manually */ + // create all the nodes + ns3::NodeContainer routers; + routers.Create(2); + ns3::NodeContainer leftleaves; + leftleaves.Create(number_of_clients); + ns3::NodeContainer rightleaves; + rightleaves.Create(number_of_clients); + + // error model + ns3::Ptr em = + ns3::CreateObject(); + em->SetAttribute("ErrorRate", ns3::DoubleValue(error_p)); + + // add the link connecting the routers + ns3::NetDeviceContainer routerdevices = + bottleNeckLink.Install(routers); + + ns3::NetDeviceContainer leftrouterdevices; + ns3::NetDeviceContainer leftleafdevices; + ns3::NetDeviceContainer rightrouterdevices; + ns3::NetDeviceContainer rightleafdevices; + + // add links on both sides + for (uint32_t i = 0; iSetAttribute("ReceiveErrorModel", + ns3::PointerValue(em)); + // add the right side links + ns3::NetDeviceContainer cright = + pointToPointLeaf.Install(routers.Get(1), rightleaves.Get(i)); + rightrouterdevices.Add(cright.Get(0)); + rightleafdevices.Add(cright.Get(1)); + cright.Get(0)->SetAttribute("ReceiveErrorModel", + ns3::PointerValue(em)); + } + + // install internet stack on all nodes + ns3::InternetStackHelper stack; + stack.Install(routers); + stack.Install(leftleaves); + stack.Install(rightleaves); + + // assign ipv4 addresses (ipv6 addresses apparently are still not fully + // supported by ns3) + ns3::Ipv4AddressHelper routerips = + ns3::Ipv4AddressHelper("10.3.0.0", "255.255.255.0"); + ns3::Ipv4AddressHelper leftips = + ns3::Ipv4AddressHelper("10.1.0.0", "255.255.255.0"); + ns3::Ipv4AddressHelper rightips = + ns3::Ipv4AddressHelper("10.2.0.0", "255.255.255.0"); + + ns3::Ipv4InterfaceContainer routerifs; + ns3::Ipv4InterfaceContainer leftleafifs; + ns3::Ipv4InterfaceContainer leftrouterifs; + ns3::Ipv4InterfaceContainer rightleafifs; + ns3::Ipv4InterfaceContainer rightrouterifs; + + // assign addresses to connection connecting routers + routerifs = routerips.Assign(routerdevices); + + // assign addresses to connection between routers and leaves + for (uint32_t i = 0; i sockptr; + unsigned int pkgsize; + float start; + + std::stringstream ss(inputlines[i]); + std::string app, transport; + ss >> app >> transport >> pkgsize >> start; + + if (transport.compare("TCP") == 0) { + // setup source socket + sockptr = + ns3::Socket::CreateSocket(leftleaves.Get(i), + ns3::TcpSocketFactory::GetTypeId()); + ns3::Ptr tcpsockptr = + ns3::DynamicCast (sockptr); + tcpsockptr->SetAttribute("SegmentSize", + ns3::UintegerValue(pkgsize)); + std::stringstream nodeidss; + nodeidss << leftleaves.Get(i)->GetId(); + std::string prefix = "/NodeList/" + nodeidss.str(); + sockptr->TraceConnect("CongestionWindow", + prefix + + "/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", + ns3::MakeCallback(&CwndTracer)); + sockptr->TraceConnect("SlowStartThreshold", + prefix + + "/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold", + ns3::MakeCallback(&SsThreshTracer)); + // setup sink + sinkApps.Add(TcpPacketSinkHelper.Install(rightleaves.Get(i))); + } else if (transport.compare("UDP") == 0) { + // setup source socket + sockptr = + ns3::Socket::CreateSocket(leftleaves.Get(i), + ns3::UdpSocketFactory::GetTypeId()); + // setup sink + sinkApps.Add(UdpPacketSinkHelper.Install(rightleaves.Get(i))); + } else { + std::cerr << "unknown transport type: " << + transport << std::endl; + exit(1); + } + + if (app.compare("LR") == 0) { + /* additionally read stop time and rate */ + float stop; + std::string rate; + ss >> stop >> rate; + ns3::Ptr app = + ns3::CreateObject (); + app->Setup(sockptr, + ns3::InetSocketAddress(rightleafifs.GetAddress(i), + port), pkgsize, + ns3::DataRate(rate)); + leftleaves.Get(i)->AddApplication(app); + app->SetStartTime(ns3::Seconds(start)); + app->SetStopTime(ns3::Seconds(stop)); + } else if (app.compare("UR") == 0) { + /* additionally read stop time */ + float stop; + ss >> stop; + ns3::Ptr app = + ns3::CreateObject (); + app->Setup(sockptr, + ns3::InetSocketAddress(rightleafifs.GetAddress(i), + port), pkgsize); + leftleaves.Get(i)->AddApplication(app); + app->SetStartTime(ns3::Seconds(start)); + app->SetStopTime(ns3::Seconds(stop)); + } else if (app.compare("LT") == 0) { + /* additionally read maxtransfer */ + unsigned int maxtransfer; + ss >> maxtransfer; + ns3::Ptr app = + ns3::CreateObject (); + app->Setup(sockptr, + ns3::InetSocketAddress(rightleafifs.GetAddress(i), + port), pkgsize, maxtransfer); + leftleaves.Get(i)->AddApplication(app); + app->SetStartTime(ns3::Seconds(start)); + } else { + std::cerr << "unknown app type: " << app << std::endl; + exit(1); + } + } + + sinkApps.Start(ns3::Seconds(0.0)); + + ns3::Ipv4GlobalRoutingHelper::PopulateRoutingTables(); + + // connect to some trace sources + ns3::Config::Connect("/NodeList/*/DeviceList/*/TxQueue/Drop", + ns3::MakeCallback(&TxQueueDropTracer)); + ns3::Config::Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSink/Rx", + ns3::MakeCallback(&PacketSinkRxTracer)); + ns3::Config::Connect("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/PhyRxDrop", + ns3::MakeCallback(&PhyRxDropTracer)); + + // pcap tracing + if (pcaptracing) + pointToPointLeaf.EnablePcapAll(argv[0], true); + + if (simstop > 0.0) { + ns3::Simulator::Stop(ns3::Seconds(simstop)); + } + ns3::Simulator::Run(); + + std::cerr << "Dumbbell Left Bottleneck NodeID " << + routers.Get(0)->GetId() << std::endl; + std::cerr << "Dumbbell Right Bottleneck NodeID " << + routers.Get(1)->GetId() << std::endl; + for (uint32_t i = 0; iGetId() << " IP Addr " << + leftleafifs.GetAddress(i) << std::endl; + } + for (uint32_t i = 0; iGetId() << " IP Addr " << rightleafifs. + GetAddress(i) << std::endl; + } + + ns3::Simulator::Destroy(); + return 0; +}