/* * 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 queuesize = 90; 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.AddValue("queue_size", "Maximum amount of packets allowed inside the queue", queuesize); cmd.Parse(argc, argv); ns3::SeedManager::SetSeed(1); ns3::SeedManager::SetRun(run); ns3::Config::SetDefault("ns3::DropTailQueue::Mode", ns3::StringValue("QUEUE_MODE_PACKETS")); ns3::Config::SetDefault("ns3::DropTailQueue::MaxPackets", ns3::UintegerValue(queuesize)); 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; }