├── docs └── img │ ├── graph-1.png │ ├── graph-2.png │ ├── graph-3.png │ └── graph-4.png ├── .gitignore ├── CMakeLists.txt ├── p2p-project.cc ├── simulation ├── tcp-tahoe-loss-recovery.cpp ├── tcp-tahoe.h ├── tcp-tahoe-loss-recovery.h ├── configuration.h ├── tcp-tahoe.cc ├── simulator-helper.h ├── tracer.h ├── configuration.cc ├── tracer.cc └── simulator-helper.cc └── README.md /docs/img/graph-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TendTo/ns3-Tahoe-vs-Reno/HEAD/docs/img/graph-1.png -------------------------------------------------------------------------------- /docs/img/graph-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TendTo/ns3-Tahoe-vs-Reno/HEAD/docs/img/graph-2.png -------------------------------------------------------------------------------- /docs/img/graph-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TendTo/ns3-Tahoe-vs-Reno/HEAD/docs/img/graph-3.png -------------------------------------------------------------------------------- /docs/img/graph-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TendTo/ns3-Tahoe-vs-Reno/HEAD/docs/img/graph-4.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ gitignore 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Compiled Object files 7 | *.slo 8 | *.lo 9 | *.o 10 | *.obj 11 | 12 | # Precompiled Headers 13 | *.gch 14 | *.pch 15 | 16 | # Compiled Dynamic libraries 17 | *.so 18 | *.dylib 19 | *.dll 20 | 21 | # Fortran module files 22 | *.mod 23 | *.smod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # CMake files 37 | CMakeCache.txt 38 | CMakeFiles/ 39 | cmake_install.cmake 40 | CTestTestfile.cmake 41 | Makefile 42 | 43 | # Qt files 44 | *.moc 45 | *.qrc 46 | *.ui 47 | *.qch 48 | 49 | # Qt Creator files 50 | *.autosave 51 | *.autosave~ 52 | 53 | # Vscode files 54 | .vscode/ 55 | 56 | # Temp files 57 | *.tmp -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Return early if no sources in the subdirectory 2 | set(main_src p2p-project) 3 | set(header_files simulation/tcp-tahoe simulation/simulator-helper simulation/configuration simulation/tracer simulation/tcp-tahoe-loss-recovery) 4 | set(source_files ${main_src} ${header_files}) 5 | set(target_prefix scratch_P2P_) 6 | 7 | get_filename_component(main_src ${main_src} ABSOLUTE) 8 | get_filename_component(scratch_absolute_directory ${main_src} DIRECTORY) 9 | string(REPLACE "${PROJECT_SOURCE_DIR}" "${CMAKE_OUTPUT_DIRECTORY}" 10 | scratch_directory ${scratch_absolute_directory} 11 | ) 12 | 13 | build_exec( 14 | EXECNAME p2p-project 15 | EXECNAME_PREFIX ${target_prefix} 16 | SOURCE_FILES ${source_files} 17 | HEADER_FILES ${header_files} 18 | LIBRARIES_TO_LINK "${ns3-libs}" "${ns3-contrib-libs}" 19 | EXECUTABLE_DIRECTORY_PATH ${scratch_directory}/ 20 | ) -------------------------------------------------------------------------------- /p2p-project.cc: -------------------------------------------------------------------------------- 1 | #include "simulation/configuration.h" 2 | #include "simulation/simulator-helper.h" 3 | #include "simulation/tracer.h" 4 | 5 | #include "ns3/core-module.h" 6 | #include "ns3/internet-module.h" 7 | #include "ns3/netanim-module.h" 8 | #include "ns3/point-to-point-helper.h" 9 | 10 | using namespace ns3; 11 | 12 | NS_LOG_COMPONENT_DEFINE("P2P-Project"); 13 | 14 | int 15 | main(int argc, char* argv[]) 16 | { 17 | // Read the configuration from the command line and use it to initialize the default values 18 | Configuration conf; 19 | ParseConsoleArgs(conf, argc, argv); 20 | InitializeDefaultConfiguration(conf); 21 | 22 | // Set up tracing 23 | Tracer tracer(conf, GraphDataUpdateType::All); 24 | 25 | SimulatorHelper simHelper(conf, tracer); 26 | simHelper.Setup(); 27 | 28 | NS_LOG_INFO("Run Simulation"); 29 | simHelper.Run(); 30 | NS_LOG_INFO("The simulation has ended"); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /simulation/tcp-tahoe-loss-recovery.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp-tahoe-loss-recovery.h" 2 | 3 | NS_LOG_COMPONENT_DEFINE("TcpTahoeLossRecovery"); 4 | NS_OBJECT_ENSURE_REGISTERED(TcpTahoeLossRecovery); 5 | 6 | TypeId 7 | TcpTahoeLossRecovery::GetTypeId() 8 | { 9 | static TypeId tid = TypeId("ns3::TcpTahoeLossRecovery") 10 | .SetParent() 11 | .SetGroupName("Internet") 12 | .AddConstructor(); 13 | return tid; 14 | } 15 | 16 | TcpTahoeLossRecovery::TcpTahoeLossRecovery() 17 | : TcpRecoveryOps() 18 | { 19 | NS_LOG_FUNCTION(this); 20 | } 21 | 22 | TcpTahoeLossRecovery::TcpTahoeLossRecovery(const TcpTahoeLossRecovery& recovery) 23 | : TcpRecoveryOps(recovery) 24 | { 25 | NS_LOG_FUNCTION(this); 26 | } 27 | 28 | TcpTahoeLossRecovery::~TcpTahoeLossRecovery() 29 | { 30 | NS_LOG_FUNCTION(this); 31 | } 32 | 33 | std::string 34 | TcpTahoeLossRecovery::GetName() const 35 | { 36 | return "TcpTahoeLossRecovery"; 37 | } 38 | 39 | void 40 | TcpTahoeLossRecovery::EnterRecovery(Ptr tcb, 41 | uint32_t dupAckCount, 42 | uint32_t unAckDataCount, 43 | uint32_t deliveredBytes) 44 | { 45 | NS_LOG_FUNCTION(this << tcb << dupAckCount << unAckDataCount << deliveredBytes); 46 | 47 | NS_LOG_INFO("Entering recovery. LastAcked sq: " << tcb->m_lastAckedSeq); 48 | NS_LOG_LOGIC("Store the ssThresh set to half of cwnd. cwnd set to one. ssThresh set to zero " 49 | "temporarily"); 50 | // Store the halved ssThresh value 51 | m_half_ssThresh = tcb->m_cWnd / 2; 52 | // Set the ssThresh to zero, to avoid the sudden increase of cwnd at the end of the recovery 53 | tcb->m_ssThresh = 0; 54 | // Set the cwnd to one 55 | tcb->m_cWnd = 1; 56 | tcb->m_cWndInfl = 0; 57 | } 58 | 59 | void 60 | TcpTahoeLossRecovery::DoRecovery(Ptr tcb, uint32_t deliveredBytes) 61 | { 62 | NS_LOG_FUNCTION(this << tcb << deliveredBytes); 63 | 64 | NS_LOG_INFO("Entering recovery. LastAcked sq: " << tcb->m_lastAckedSeq); 65 | NS_LOG_LOGIC("Apply tcp tahoe congestion control algorithm"); 66 | } 67 | 68 | void 69 | TcpTahoeLossRecovery::ExitRecovery(Ptr tcb) 70 | { 71 | NS_LOG_FUNCTION(this << tcb); 72 | 73 | NS_LOG_INFO("Exiting recovery"); 74 | // Restore the ssThresh value to the halved value 75 | tcb->m_ssThresh = m_half_ssThresh; 76 | } 77 | 78 | Ptr 79 | TcpTahoeLossRecovery::Fork() 80 | { 81 | return CopyObject(this); 82 | } 83 | -------------------------------------------------------------------------------- /simulation/tcp-tahoe.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_TAHOE_H 2 | #define TCP_TAHOE_H 3 | 4 | #include "ns3/tcp-congestion-ops.h" 5 | #include "ns3/tcp-socket-state.h" 6 | 7 | using namespace ns3; 8 | 9 | /** 10 | * @brief TcpTahoe class. 11 | * Tahoe congestion control algorithm. 12 | * This class implement the Tahoe congestion control type and it mimics the first TCP variant with 13 | * in-built congestion control algorithms. 14 | */ 15 | class TcpTahoe : public TcpCongestionOps 16 | { 17 | public: 18 | /** 19 | * @brief Get the type ID. 20 | * @return the object TypeId. 21 | */ 22 | static TypeId GetTypeId(void); 23 | /** 24 | * @brief Create an unbound tcp socket. 25 | */ 26 | TcpTahoe(); 27 | /** 28 | * @brief Copy constructor. 29 | * @param sock the object to copy. 30 | */ 31 | TcpTahoe(const TcpTahoe& sock); 32 | /** 33 | * @brief Get the name of the class. 34 | * @return name of the class. 35 | */ 36 | std::string GetName() const override; 37 | 38 | /** 39 | * @brief Congestion avoidance algorithm implementation. 40 | * Called each time the congestion window is supposed to be increased. 41 | * @param tcb internal congestion state. 42 | * @param segmentsAcked count of segments acked. 43 | */ 44 | void IncreaseWindow(Ptr tcb, uint32_t segmentsAcked) override; 45 | /** 46 | * @brief Get the slow start threshold after a loss event. 47 | * @param tcb internal congestion state. 48 | * @param bytesInFlight total bytes in flight. 49 | * @return ssThresh value. 50 | */ 51 | uint32_t GetSsThresh(Ptr tcb, uint32_t bytesInFlight) override; 52 | /** 53 | * @brief Copy the congestion control algorithm across sockets. 54 | * @return a pointer of the copied object. 55 | */ 56 | Ptr Fork() override; 57 | 58 | protected: 59 | /** 60 | * @brief Slow start phase handler. 61 | * Each time the method is invoked, the congestion window is increased by (segmentAcked * 62 | * segmentSize). 63 | * @param tcb internal congestion state. 64 | * @param segmentsAcked count of segments acked. 65 | */ 66 | void SlowStart(Ptr tcb, uint32_t segmentsAcked); 67 | /** 68 | * @brief Congestion avoidance phase handler. 69 | * Each time the method is invoked, the congestion window is increased by (segmentAcked * 70 | * segmentSize / congestion window). 71 | * It stores the number of segments acked if they are not enough to increase the congestion 72 | * window of a segment size. 73 | * @param tcb internal congestion state. 74 | * @param segmentsAcked count of segments acked. 75 | */ 76 | void CongestionAvoidance(Ptr tcb, uint32_t segmentsAcked); 77 | 78 | private: 79 | uint32_t m_cWndCnt = 0; //!< Counter for the number of segments acked 80 | }; 81 | 82 | #endif /* TCP_TAHOE_H */ 83 | -------------------------------------------------------------------------------- /simulation/tcp-tahoe-loss-recovery.h: -------------------------------------------------------------------------------- 1 | #ifndef P2P_SIMULATION_TCP_TAHOE_LOSS_RECOVERY_H 2 | #define P2P_SIMULATION_TCP_TAHOE_LOSS_RECOVERY_H 3 | 4 | #include "tcp-tahoe.h" 5 | 6 | #include "ns3/core-module.h" 7 | #include "ns3/tcp-congestion-ops.h" 8 | #include "ns3/tcp-recovery-ops.h" 9 | #include "ns3/tcp-socket-base.h" 10 | 11 | using namespace ns3; 12 | 13 | /** 14 | * @brief TcpTahoeLossRecovery class. 15 | * It is used to implement the loss recovery part of the TCP Tahoe congestion control algorithm. 16 | * It doesn't use the fast recovery mechanism, present in newer TCP versions. 17 | * Instead, it brutally sets the ssthresh to half of the current cwnd and the cwnd to 1. 18 | */ 19 | class TcpTahoeLossRecovery : public TcpRecoveryOps 20 | { 21 | public: 22 | /** 23 | * @brief Get the type ID. 24 | * @return the object TypeId. 25 | */ 26 | static TypeId GetTypeId(); 27 | 28 | /** 29 | * @brief Constructor. 30 | */ 31 | TcpTahoeLossRecovery(); 32 | 33 | /** 34 | * @brief Copy constructor. 35 | * @param recovery object to copy. 36 | */ 37 | TcpTahoeLossRecovery(const TcpTahoeLossRecovery& recovery); 38 | 39 | /** 40 | * @brief Constructor. 41 | */ 42 | ~TcpTahoeLossRecovery() override; 43 | 44 | /** 45 | * @brief Get the name of the class. 46 | * @return name of the class. 47 | */ 48 | std::string GetName() const override; 49 | 50 | /** 51 | * @brief A packet has been lost, so the recovery mechanism is triggered. 52 | * @param tcb internal congestion state. 53 | * @param dupAckCount duplicate acknowledgement count. 54 | * @param unAckDataCount total bytes of data unacknowledged. 55 | * @param deliveredBytes bytes (S)ACKed in the last (S)ACK. 56 | */ 57 | void EnterRecovery(Ptr tcb, 58 | uint32_t dupAckCount, 59 | uint32_t unAckDataCount, 60 | uint32_t deliveredBytes) override; 61 | /** 62 | * @brief Performs a normal Tcp Tahoe increment window procedure. 63 | * @param tcb internal congestion state. 64 | * @param deliveredBytes bytes (S)ACKed in the last (S)ACK. 65 | */ 66 | void DoRecovery(Ptr tcb, uint32_t deliveredBytes) override; 67 | /** 68 | * @brief Performs cwnd adjustments at the end of recovery. 69 | * The function is called when the TcpSocketState is changed from CA_RECOVERY. 70 | * @param tcb internal congestion state. 71 | */ 72 | void ExitRecovery(Ptr tcb) override; 73 | /** 74 | * @brief Copy the recovery algorithm across socket. 75 | * @return a pointer of the copied object. 76 | */ 77 | Ptr Fork() override; 78 | 79 | private: 80 | uint32_t m_half_ssThresh; //!< ssthresh value calculated by having the current cwnd. 81 | TcpTahoe m_tahoeCongestionControl; //!< The TCP Tahoe congestion control algorithm. 82 | }; 83 | 84 | #endif /* P2P_SIMULATION_TCP_TAHOE_LOSS_RECOVERY_H */ -------------------------------------------------------------------------------- /simulation/configuration.h: -------------------------------------------------------------------------------- 1 | #ifndef P2P_SIMULATION_CONFIGURATION_H 2 | #define P2P_SIMULATION_CONFIGURATION_H 3 | 4 | #include "tcp-tahoe-loss-recovery.h" 5 | #include "tcp-tahoe.h" 6 | 7 | #include "ns3/core-module.h" 8 | #include "ns3/error-model.h" 9 | #include "ns3/ipv4-header.h" 10 | 11 | using namespace ns3; 12 | 13 | /** 14 | * @brief Struct to store the configuration of the simulation. 15 | */ 16 | struct Configuration 17 | { 18 | /********************************* 19 | *TCP Configuration. 20 | *********************************/ 21 | uint32_t n_tcp_tahoe = 1; //!< Number of TCP Tahoe nodes. 22 | uint32_t n_tcp_reno = 1; //!< Number of TCP Reno nodes. 23 | uint32_t snd_buf_size = 131072; //!< Send buffer size. 24 | uint32_t rcv_buf_size = 131072; //!< Receive buffer size. 25 | uint32_t initial_cwnd = 1; //!< Initial congestion window. 26 | uint32_t initial_ssthresh = 65535; //!< Initial slow start threshold. 27 | uint32_t mtu_bytes = 1500; //!< MTU in bytes. 28 | uint32_t adu_bytes = 0; //!< Actual segment size (ADU) in bytes. 29 | bool sack = true; //!< Whether to enable Tcp SACK. 30 | bool nagle = false; //!< Whether to disable Nagle's algorithm. 31 | /********************************* 32 | *Channel Configuration. 33 | *********************************/ 34 | double error_p = 0.0; //!< Error rate of the channel. 35 | std::string s_bandwidth = "10Mbps"; //!< Bandwidth of the channel of the sender. 36 | std::string s_delay = "40ms"; //!< Delay of the channel of the sender. 37 | std::string r_bandwidth = "10Mbps"; //!< Bandwidth of the channel of the receiver. 38 | std::string r_delay = "40ms"; //!< Delay of the channel of the receiver. 39 | uint32_t tcp_queue_size = 25; //!< Size of the queue at the TCP level. 40 | // https://groups.google.com/g/ns-3-users/c/e15_YvL-7v0 41 | // uint32_t device_queue_size = 100; 42 | /********************************* 43 | * Simulation Configuration. 44 | *********************************/ 45 | uint32_t run = 0; //!< Run identifier. Used to seed the random number generator. 46 | double duration = 3.0; //!< Duration of the simulation in seconds. 47 | uint64_t max_mbytes_to_send = 0; //!< Maximum number of megabytes to send. 0 means unlimited. 48 | /********************************* 49 | * Tracing Configuration. 50 | *********************************/ 51 | std::string prefix_file_name = "P2P-project"; //!< Prefix of the output trace file. 52 | std::string graph_output = "png"; //!< Output format of the graph. Can be "png" or "svg". 53 | bool pcap_tracing = false; //!< Enable or disable PCAP tracing. 54 | bool ascii_tracing = false; //!< Enable or disable ASCII tracing. 55 | }; 56 | 57 | /** 58 | * @brief Overload of the << operator to print the configuration. 59 | * @param os Output stream. 60 | * @param conf Configuration. 61 | * @return Output stream. 62 | */ 63 | std::ostream& operator<<(std::ostream& os, const Configuration& conf); 64 | 65 | /** 66 | * @brief Parse the command line arguments and store them in the configuration. 67 | * @param conf Configuration object to store the values in. 68 | * @param argc Number of arguments. 69 | * @param argv Arguments. 70 | */ 71 | void ParseConsoleArgs(Configuration& conf, int argc, char* argv[]); 72 | /** 73 | * @brief Initialize the default attributes of the simulation with the configuration. 74 | * @param configuration Configuration. 75 | */ 76 | void InitializeDefaultConfiguration(const Configuration& configuration); 77 | 78 | #endif // P2P_SIMULATION_CONFIGURATION_H -------------------------------------------------------------------------------- /simulation/tcp-tahoe.cc: -------------------------------------------------------------------------------- 1 | #include "tcp-tahoe.h" 2 | 3 | #include "ns3/log.h" 4 | #include "ns3/simulator.h" 5 | 6 | NS_LOG_COMPONENT_DEFINE("TcpTahoe"); 7 | NS_OBJECT_ENSURE_REGISTERED(TcpTahoe); 8 | 9 | TcpTahoe::TcpTahoe() 10 | : TcpCongestionOps() 11 | { 12 | } 13 | 14 | TcpTahoe::TcpTahoe(const TcpTahoe& socket) 15 | : TcpCongestionOps(socket) 16 | { 17 | } 18 | 19 | ns3::TypeId 20 | TcpTahoe::GetTypeId() 21 | { 22 | static TypeId tid = TypeId("ns3::TcpTahoe") 23 | .SetParent() 24 | .SetGroupName("Internet") 25 | .AddConstructor(); 26 | return tid; 27 | } 28 | 29 | std::string 30 | TcpTahoe::GetName() const 31 | { 32 | return "TcpTahoe"; 33 | } 34 | 35 | void 36 | TcpTahoe::IncreaseWindow(Ptr tcb, uint32_t segmentsAcked) 37 | { 38 | NS_LOG_FUNCTION(this << tcb << segmentsAcked); 39 | 40 | if (tcb->m_cWnd < tcb->m_ssThresh) // The cwnd is less than the ssThresh => slow start 41 | { 42 | NS_LOG_DEBUG("In slow start, m_cWnd " << tcb->m_cWnd << " m_ssThresh " << tcb->m_ssThresh); 43 | SlowStart(tcb, segmentsAcked); 44 | } 45 | else // The cwnd is greater than or equal to the ssThresh => congestion avoidance 46 | { 47 | NS_LOG_DEBUG("In cong. avoidance, m_cWnd " << tcb->m_cWnd << " m_ssThresh " 48 | << tcb->m_ssThresh); 49 | CongestionAvoidance(tcb, segmentsAcked); 50 | } 51 | } 52 | 53 | uint32_t 54 | TcpTahoe::GetSsThresh(Ptr tcb, uint32_t bytesInFlight) 55 | { 56 | NS_LOG_FUNCTION(this << tcb << bytesInFlight); 57 | 58 | return std::max(2 * tcb->m_segmentSize, tcb->m_cWnd / 2); 59 | } 60 | 61 | ns3::Ptr 62 | TcpTahoe::Fork() 63 | { 64 | return CopyObject(this); 65 | } 66 | 67 | void 68 | TcpTahoe::SlowStart(Ptr tcb, uint32_t segmentsAcked) 69 | { 70 | NS_LOG_FUNCTION(this << tcb << segmentsAcked); 71 | 72 | if (segmentsAcked >= 1) 73 | { 74 | uint32_t sndCwnd = tcb->m_cWnd; 75 | // Set the cwnd to the minimum of the following: 76 | // 1. The current cwnd + the number of segments acked * segment size 77 | // 2. The ssThresh 78 | uint32_t realSegmentsAcked = std::min(segmentsAcked, tcb->m_bytesInFlight.Get() / tcb->m_segmentSize + 1); 79 | tcb->m_cWnd = 80 | std::min((sndCwnd + (realSegmentsAcked * tcb->m_segmentSize)), (uint32_t)tcb->m_ssThresh); 81 | NS_LOG_INFO("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh " 82 | << tcb->m_ssThresh); 83 | } 84 | } 85 | 86 | void 87 | TcpTahoe::CongestionAvoidance(Ptr tcb, uint32_t segmentsAcked) 88 | { 89 | NS_LOG_FUNCTION(this << tcb << segmentsAcked); 90 | 91 | // Number of packets in the cwnd 92 | uint32_t w = tcb->m_cWnd / tcb->m_segmentSize; 93 | 94 | // Floor w to 1 if w == 0 95 | if (w == 0) 96 | w = 1; 97 | 98 | NS_LOG_DEBUG("w in segments " << w << " m_cWndCnt " << m_cWndCnt << " segments acked " 99 | << segmentsAcked); 100 | // Increase m_cWndCnt by the number of segments acked 101 | m_cWndCnt += segmentsAcked; 102 | 103 | if (m_cWndCnt >= w) 104 | { 105 | // Floor value of (segments_acked / segments in cwnd) 106 | uint32_t delta = m_cWndCnt / w; 107 | // Increase the cwnd by the (segments acked * segment size / segments in cwnd) 108 | tcb->m_cWnd += delta * tcb->m_segmentSize; 109 | // Remove the number of segments used to increase cwnd from m_cWndCnt 110 | m_cWndCnt -= delta * w; 111 | NS_LOG_DEBUG("Subtracting delta * w from m_cWndCnt " << delta * w); 112 | } 113 | NS_LOG_DEBUG("At end of CongestionAvoidance(), m_cWnd: " << tcb->m_cWnd 114 | << " m_cWndCnt: " << m_cWndCnt); 115 | } 116 | -------------------------------------------------------------------------------- /simulation/simulator-helper.h: -------------------------------------------------------------------------------- 1 | #ifndef P2P_SIMULATION_SIMULATOR_HELPER_H 2 | #define P2P_SIMULATION_SIMULATOR_HELPER_H 3 | 4 | #include "configuration.h" 5 | #include "tracer.h" 6 | 7 | #include "ns3/bulk-send-helper.h" 8 | #include "ns3/core-module.h" 9 | #include "ns3/internet-module.h" 10 | #include "ns3/packet-sink-helper.h" 11 | #include "ns3/point-to-point-helper.h" 12 | #include "ns3/point-to-point-net-device.h" 13 | #include "ns3/traffic-control-helper.h" 14 | 15 | using namespace ns3; 16 | 17 | /** 18 | * @brief SimulatorHelper class. 19 | * It is used to setup and run the simulation. 20 | * It creates nodes, channels, applications, configures them and initializes tracing. 21 | * Using the private properties, it can easily be extended be used to run multiple simulations with 22 | * different configurations. 23 | * 24 | * The simulation creates the following network configuration: 25 | * 26 | * n_1 ---- 27 | * \ 28 | * n_2 ---- g ---- r 29 | * / 30 | * n_n ---- 31 | * 32 | * n_i: sender node 33 | * g: gateway node 34 | * r: receiver node 35 | * 36 | * Each sender node is connected to the gateway node with a point-to-point channel. 37 | * The gateway node is connected to the receiver node with a point-to-point channel. 38 | * Both gateway and receiver nodes use a RED queue and may drop packets. 39 | */ 40 | class SimulatorHelper 41 | { 42 | public: 43 | /** 44 | * @brief SimulatorHelper constructor. 45 | * @param conf Simulation configuration. 46 | * @param tracer Simulation tracer. 47 | */ 48 | SimulatorHelper(const Configuration& conf, Tracer& tracer); 49 | 50 | /** 51 | * @brief Setup the simulation. 52 | * It creates nodes, channels, applications, configures them and initializes tracing. 53 | */ 54 | void Setup(); 55 | /** 56 | * @brief Start the simulation. 57 | * Can be called only after Setup(). 58 | */ 59 | void Run(); 60 | 61 | private: 62 | /** 63 | * @brief Creates the nodes. 64 | * It creates n_flows senders, 1 receiver and 1 gateway. 65 | */ 66 | void SetupNodes(); 67 | /** 68 | * @brief Creates the sender channel. 69 | * It creates a point-to-point channel between each sender and the gateway. 70 | * Sets the data rate and delay of the channel. 71 | */ 72 | void SetupSenderChannel(); 73 | /** 74 | * @brief Creates the receiver channel. 75 | * It creates a point-to-point channel between the gateway and the receiver. 76 | * Sets the data rate and delay of the channel, as well as the error rate error_p, and the 77 | * number of packets the RED queue will accept at most. 78 | */ 79 | void SetupReceiverChannel(); 80 | /** 81 | * @brief Creates the sender applications. 82 | * It creates a BulkSendApplication for each sender, all sending towards the receiver. 83 | * Sets the packet size, the number of bytes to send, the start time and the stop time of the 84 | * application. 85 | */ 86 | void SetupSenderApplications(); 87 | /** 88 | * @brief Creates the receiver applications. 89 | * It creates a PacketSinkApplication for the receiver, accepting connections from all senders. 90 | * Sets the start time and the stop time of the application. 91 | */ 92 | void SetupReceiverApplications(); 93 | /** 94 | * @brief Enables tracing. 95 | * It schedules the tracing methods used to track the trace sources. 96 | * It also initializes both ascii and pcap tracing for the sender and receiver channels, if 97 | * enabled. 98 | */ 99 | void SetupTracing(); 100 | 101 | private: 102 | const uint32_t m_port; //!< Port used by the receiver application. 103 | const Configuration& m_conf; //!< Simulation configuration. 104 | bool m_isInitialized; //!< True if the simulation has been initialized. 105 | Tracer m_tracer; //!< Simulation tracer. 106 | NodeContainer m_senders; //!< Senders nodes. 107 | NodeContainer m_receivers; //!< Receiver node. 108 | NodeContainer m_gateway; //!< Gateway node. 109 | Ipv4AddressHelper m_ipv4Helper; //!< Ipv4 address generator. 110 | PointToPointHelper m_s_pointToPoint; //!< Sender channel helper. 111 | PointToPointHelper m_r_pointToPoint; //!< Receiver channel helper. 112 | }; 113 | 114 | #endif /* P2P_SIMULATION_SIMULATOR_HELPER_H */ -------------------------------------------------------------------------------- /simulation/tracer.h: -------------------------------------------------------------------------------- 1 | #ifndef P2P_SIMULATION_TRACER_H 2 | #define P2P_SIMULATION_TRACER_H 3 | 4 | #include "configuration.h" 5 | 6 | #include "ns3/applications-module.h" 7 | #include "ns3/core-module.h" 8 | #include "ns3/gnuplot.h" 9 | #include "ns3/socket.h" 10 | 11 | using namespace ns3; 12 | 13 | /** 14 | * @brief It specifies when the tracer should update the graph data. 15 | * A new point in the graph is added when the specified update type occurs. 16 | * The flags can be combined to specify multiple update types. 17 | */ 18 | enum GraphDataUpdateType 19 | { 20 | None = 1 << 0, 21 | Cwnd = 1 << 1, 22 | SsThresh = 1 << 2, 23 | QueueSize = 1 << 3, 24 | All = (1 << 4) - 1 25 | }; 26 | 27 | /** 28 | * @brief Allows to combine GraphDataUpdateType flags using the bitwise OR operator. 29 | * @param lhs left hand side GraphDataUpdateType 30 | * @param rhs right hand side GraphDataUpdateType 31 | * @return combined GraphDataUpdateType 32 | */ 33 | inline GraphDataUpdateType operator|(const GraphDataUpdateType& lhs, 34 | const GraphDataUpdateType& rhs); 35 | 36 | /** 37 | * @brief Point in the graph for a sender node. 38 | * It contains the congestion window and the slow start threshold of the node at the given time. 39 | */ 40 | struct SenderGraphData 41 | { 42 | uint32_t time; 43 | uint32_t cwnd; 44 | uint32_t ssthresh; 45 | }; 46 | 47 | /** 48 | * @brief Point in the graph for the receiver node. 49 | * It contains the queue size of the node at the given time. 50 | */ 51 | struct ReceiverGraphData 52 | { 53 | uint32_t time; 54 | uint32_t tcpQueueSize; 55 | }; 56 | 57 | /** 58 | * @brief Tracer class. 59 | * It is used to trace the simulation and aggregate the data. 60 | * The data will later be used to create the graphs. 61 | * It uses the ns3 callback system to spy on the trace sources. 62 | */ 63 | class Tracer 64 | { 65 | public: 66 | /** 67 | * @brief Tracer constructor. 68 | * @param conf simulation configuration. 69 | */ 70 | Tracer(const Configuration& conf); 71 | /** 72 | * @brief Trace constructor. 73 | * @param conf simulation configuration. 74 | * @param updateType type of update the tracer will use to aggregate the data. 75 | */ 76 | Tracer(const Configuration& conf, const GraphDataUpdateType updateType); 77 | 78 | /** 79 | * @brief Sender graph data getter. 80 | * @return sender graph data. 81 | */ 82 | const std::map>& GetSenderGraphData() const; 83 | /** 84 | * @brief Receiver graph data getter. 85 | * @return receiver graph data. 86 | */ 87 | const std::vector& GetReceiverGraphData() const; 88 | 89 | /** 90 | * @brief Schedule the tracing. 91 | * Since some resources are not created at the beginning of the simulation, 92 | * the tracing must be scheduled to be attached after the simulation has started. 93 | */ 94 | void ScheduleTracing(); 95 | /** 96 | * @brief Trace the congestion window. 97 | * @param ctx id of the node. 98 | * @param oldval old congestion window value. 99 | * @param newval new congestion window value. 100 | */ 101 | void CwndTracer(std::string ctx, uint32_t oldval, uint32_t newval); 102 | /** 103 | * @brief Trace the slow start threshold. 104 | * @param ctx id of the node. 105 | * @param oldval old slow start threshold value. 106 | * @param newval new slow start threshold value. 107 | */ 108 | void SsThreshTracer(std::string ctx, uint32_t oldval, uint32_t newval); 109 | /** 110 | * @brief Trace the queue size. 111 | * @param oldval old queue size. 112 | * @param newval new queue size. 113 | */ 114 | void TcpQueueTracer(uint32_t oldval, uint32_t newval); 115 | /** 116 | * @brief Print the aggregated data to the console. 117 | */ 118 | void PrintGraphData() const; 119 | /** 120 | * @brief Print the aggregated data to a file. 121 | * The file can be later be processed by gnuplot to create a .png file. 122 | * `gnuplot .plot` 123 | */ 124 | void PrintGraphDataToFile() const; 125 | 126 | protected: 127 | /** 128 | * @brief Extract the node id from the context string. 129 | * @param ctx path of the trace source. 130 | * @return id of the node. 131 | */ 132 | static uint32_t GetNodeIdFromContext(std::string ctx); 133 | /** 134 | * @brief Update the graph data, by adding a new point to the graph. 135 | * @param nodeId id of the node the data belongs to. 136 | */ 137 | void UpdateGraphData(uint32_t nodeId); 138 | 139 | private: 140 | const Configuration& m_conf; //!< Configuration 141 | const GraphDataUpdateType m_updateType; //!< Aggregation type 142 | std::map m_cwndMap; //!< Congestion window outut 143 | std::map m_ssThreshMap; //!< Slow start threshold outut 144 | std::map> 145 | m_senderGraphData; //!< Aggregated sender data outut 146 | std::vector m_receiverGraphData; //!< Aggregated receiver data outut 147 | }; 148 | 149 | #endif /* P2P_SIMULATION_TRACER_H */ -------------------------------------------------------------------------------- /simulation/configuration.cc: -------------------------------------------------------------------------------- 1 | #include "configuration.h" 2 | 3 | NS_LOG_COMPONENT_DEFINE("Configuration"); 4 | 5 | static uint32_t 6 | GetTcpSegmentSize(const Configuration& conf) 7 | { 8 | Header* temp_header = new Ipv4Header(); 9 | uint32_t ip_header = temp_header->GetSerializedSize(); 10 | NS_LOG_LOGIC("IP Header size is: " << ip_header); 11 | delete temp_header; 12 | temp_header = new TcpHeader(); 13 | uint32_t tcp_header = temp_header->GetSerializedSize(); 14 | NS_LOG_LOGIC("TCP Header size is: " << tcp_header); 15 | delete temp_header; 16 | uint32_t tcp_adu_size = conf.mtu_bytes - 20 - (ip_header + tcp_header); 17 | NS_LOG_LOGIC("TCP ADU size is: " << tcp_adu_size); 18 | return tcp_adu_size; 19 | } 20 | 21 | static void 22 | SetTcpAttributes(const Configuration& conf) 23 | { 24 | // The maximum transmit buffer size 25 | Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(conf.snd_buf_size)); 26 | // The maximum receive buffer size 27 | Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(conf.rcv_buf_size)); 28 | // The maximum segment size 29 | Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(conf.adu_bytes)); 30 | // The initial congestion window 31 | Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(conf.initial_cwnd)); 32 | // The initial slow start threshold 33 | Config::SetDefault("ns3::TcpSocket::InitialSlowStartThreshold", 34 | UintegerValue(conf.initial_ssthresh)); 35 | // Disable Nagle's algorithm 36 | Config::SetDefault("ns3::TcpSocket::TcpNoDelay", BooleanValue(!conf.nagle)); 37 | // Enable SACK 38 | Config::SetDefault("ns3::TcpSocketBase::Sack", BooleanValue(conf.sack)); 39 | } 40 | 41 | static void 42 | SetQueueAttributes(const Configuration& conf) 43 | { 44 | // The maximum number of packets that can be queued 45 | Config::SetDefault("ns3::RedQueueDisc::MaxSize", 46 | StringValue(std::to_string(conf.tcp_queue_size) + "p")); 47 | } 48 | 49 | std::ostream& 50 | operator<<(std::ostream& os, const Configuration& conf) 51 | { 52 | return os << "Configuration: {" << std::endl 53 | << "\tNumber of Tcp Tahoe nodes: " << conf.n_tcp_tahoe << std::endl 54 | << "\tNumber of Tcp Reno nodes: " << conf.n_tcp_reno << std::endl 55 | << "\tError probability: " << conf.error_p << std::endl 56 | << "\tSender bandwidth: " << conf.s_bandwidth << std::endl 57 | << "\tSender delay " << conf.s_delay << std::endl 58 | << "\tReceiver bandwidth: " << conf.r_bandwidth << std::endl 59 | << "\tReceiver delay: " << conf.r_delay << std::endl 60 | << "\tTracing: " << conf.ascii_tracing << std::endl 61 | << "\tPrefix file name: " << conf.prefix_file_name << std::endl 62 | << "\tMegabytes to send (MB): " << conf.max_mbytes_to_send << std::endl 63 | << "\tMTU (bytes): " << conf.mtu_bytes << std::endl 64 | << "\tDuration (s): " << conf.duration << std::endl 65 | << "\tRun: " << conf.run << std::endl 66 | << "\tGraph output: " << conf.graph_output << std::endl 67 | << "\tSack: " << conf.sack << std::endl 68 | << "\tPcap: " << conf.pcap_tracing << std::endl 69 | << "}" << std::endl; 70 | } 71 | 72 | void 73 | ParseConsoleArgs(Configuration& conf, int argc, char* argv[]) 74 | { 75 | CommandLine cmd(__FILE__); 76 | cmd.AddValue("n_tcp_tahoe", "Number of Tcp Tahoe nodes", conf.n_tcp_tahoe); 77 | cmd.AddValue("n_tcp_reno", "Number of Tcp Reno nodes", conf.n_tcp_reno); 78 | cmd.AddValue("s_buf_size", "Sender buffer size (bytes)", conf.snd_buf_size); 79 | cmd.AddValue("r_buf_size", "Receiver buffer size (bytes)", conf.rcv_buf_size); 80 | cmd.AddValue("cwnd", "Initial congestion window (segments)", conf.initial_cwnd); 81 | cmd.AddValue("ssthresh", "Initial slow start threshold (segments)", conf.initial_ssthresh); 82 | cmd.AddValue("mtu", "Size of IP packets to send (bytes)", conf.mtu_bytes); 83 | cmd.AddValue("sack", "Enable SACK", conf.sack); 84 | cmd.AddValue("nagle", "Enable Nagle's algorithm", conf.nagle); 85 | cmd.AddValue("error_p", "Packet error rate", conf.error_p); 86 | cmd.AddValue("s_bandwidth", "Sender link bandwidth", conf.s_bandwidth); 87 | cmd.AddValue("s_delay", "Sender link delay", conf.s_delay); 88 | cmd.AddValue("r_bandwidth", "Receiver link bandwidth", conf.r_bandwidth); 89 | cmd.AddValue("r_delay", "Receiver link delay", conf.r_delay); 90 | cmd.AddValue("tcp_queue_size", "TCP queue size (packets)", conf.tcp_queue_size); 91 | cmd.AddValue("run", "Run id", conf.run); 92 | cmd.AddValue("duration", "Duration of the simulation (s)", conf.duration); 93 | cmd.AddValue("max_mbytes_to_send", 94 | "Maximum number of megabytes to send (MB)", 95 | conf.max_mbytes_to_send); 96 | cmd.AddValue("prefix_file_name", "Prefix file name", conf.prefix_file_name); 97 | cmd.AddValue("graph_output", "The type of image to output: png, svg", conf.graph_output); 98 | cmd.AddValue("ascii_tracing", "Enable ASCII tracing", conf.ascii_tracing); 99 | cmd.AddValue("pcap_tracing", "Enable Pcap tracing", conf.pcap_tracing); 100 | cmd.Parse(argc, argv); 101 | 102 | conf.adu_bytes = GetTcpSegmentSize(conf); 103 | 104 | NS_LOG_INFO(conf); 105 | } 106 | 107 | void 108 | InitializeDefaultConfiguration(const Configuration& conf) 109 | { 110 | // Set the random generation. If the run id is the same, the randomness will be the same 111 | SeedManager::SetSeed(1); 112 | SeedManager::SetRun(conf.run); 113 | 114 | SetTcpAttributes(conf); 115 | SetQueueAttributes(conf); 116 | } 117 | -------------------------------------------------------------------------------- /simulation/tracer.cc: -------------------------------------------------------------------------------- 1 | #include "tracer.h" 2 | 3 | NS_LOG_COMPONENT_DEFINE("Tracer"); 4 | 5 | inline GraphDataUpdateType 6 | operator|(const GraphDataUpdateType& lhs, const GraphDataUpdateType& rhs) 7 | { 8 | return static_cast(static_cast(lhs) | static_cast(rhs)); 9 | } 10 | 11 | Tracer::Tracer(const Configuration& conf) 12 | : m_conf(conf), 13 | m_updateType(GraphDataUpdateType::All) 14 | { 15 | } 16 | 17 | Tracer::Tracer(const Configuration& conf, const GraphDataUpdateType updateType) 18 | : m_conf(conf), 19 | m_updateType(updateType) 20 | { 21 | } 22 | 23 | void 24 | Tracer::ScheduleTracing() 25 | { 26 | NS_LOG_FUNCTION(this); 27 | 28 | for (uint32_t i = 0; i < m_conf.n_tcp_tahoe + m_conf.n_tcp_reno; ++i) 29 | { 30 | Config::Connect("/NodeList/" + std::to_string(i) + 31 | "/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", 32 | MakeCallback(&Tracer::CwndTracer, this)); 33 | Config::Connect("/NodeList/" + std::to_string(i) + 34 | "/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold", 35 | MakeCallback(&Tracer::SsThreshTracer, this)); 36 | } 37 | 38 | NS_LOG_INFO("Tracing scheduled"); 39 | } 40 | 41 | const std::map>& 42 | Tracer::GetSenderGraphData() const 43 | { 44 | return m_senderGraphData; 45 | } 46 | 47 | const std::vector& 48 | Tracer::GetReceiverGraphData() const 49 | { 50 | return m_receiverGraphData; 51 | } 52 | 53 | void 54 | Tracer::CwndTracer(std::string ctx, uint32_t oldval, uint32_t newval) 55 | { 56 | NS_LOG_FUNCTION(this << ctx << oldval << newval); 57 | 58 | uint32_t nodeId = GetNodeIdFromContext(ctx); 59 | m_cwndMap[nodeId] = newval; 60 | NS_LOG_DEBUG("Node: " << nodeId << " Cwnd: " << newval); 61 | 62 | if (m_updateType & GraphDataUpdateType::Cwnd) 63 | UpdateGraphData(nodeId); 64 | } 65 | 66 | void 67 | Tracer::SsThreshTracer(std::string ctx, uint32_t oldval, uint32_t newval) 68 | { 69 | NS_LOG_FUNCTION(this << ctx << oldval << newval); 70 | 71 | if (newval == 0) 72 | return; 73 | uint32_t nodeId = GetNodeIdFromContext(ctx); 74 | m_ssThreshMap[nodeId] = newval; 75 | NS_LOG_DEBUG("Node: " << nodeId << " SsThresh: " << newval); 76 | 77 | if (m_updateType & GraphDataUpdateType::SsThresh) 78 | UpdateGraphData(nodeId); 79 | } 80 | 81 | void 82 | Tracer::TcpQueueTracer(uint32_t oldval, uint32_t newval) 83 | { 84 | NS_LOG_FUNCTION(this << oldval << newval); 85 | 86 | if (!(m_updateType & GraphDataUpdateType::QueueSize)) 87 | return; 88 | 89 | ReceiverGraphData graphData = {static_cast(Simulator::Now().GetMilliSeconds()), 90 | newval}; 91 | m_receiverGraphData.push_back(graphData); 92 | NS_LOG_DEBUG("Time: " << graphData.time << " TcpQueueSize: " << graphData.tcpQueueSize); 93 | } 94 | 95 | uint32_t 96 | Tracer::GetNodeIdFromContext(std::string context) 97 | { 98 | NS_LOG_FUNCTION(context); 99 | 100 | std::size_t const n1 = context.find_first_of('/', 1); 101 | std::size_t const n2 = context.find_first_of('/', n1 + 1); 102 | return std::stoul(context.substr(n1 + 1, n2 - n1 - 1)); 103 | } 104 | 105 | void 106 | Tracer::UpdateGraphData(uint32_t nodeId) 107 | { 108 | NS_LOG_FUNCTION(this << nodeId); 109 | SenderGraphData graphData = { 110 | static_cast(Simulator::Now().GetMilliSeconds()), 111 | m_cwndMap.count(nodeId) == 0 ? m_conf.initial_cwnd : m_cwndMap.at(nodeId), 112 | m_ssThreshMap.count(nodeId) == 0 ? m_conf.initial_ssthresh : m_ssThreshMap.at(nodeId)}; 113 | m_senderGraphData[nodeId].push_back(graphData); 114 | 115 | NS_LOG_DEBUG("Node: " << nodeId << " Time: " << graphData.time << " Cwnd: " << graphData.cwnd 116 | << " SsThresh: " << graphData.ssthresh); 117 | } 118 | 119 | void 120 | Tracer::PrintGraphData() const 121 | { 122 | std::cout << "============= Results =============" << std::endl; 123 | for (const auto& [nodeId, graphDataVector] : m_senderGraphData) 124 | { 125 | std::cout << "Node: " << nodeId << std::endl; 126 | for (const auto& [time, cwnd, ssthresh] : graphDataVector) 127 | { 128 | std::cout << "\tTime: " << time << " Cwnd: " << cwnd / m_conf.adu_bytes 129 | << " SsThresh: " << ssthresh / m_conf.adu_bytes << std::endl; 130 | } 131 | } 132 | std::cout << "===================================" << std::endl; 133 | } 134 | 135 | void 136 | Tracer::PrintGraphDataToFile() const 137 | { 138 | Gnuplot2dDataset::SetDefaultStyle(Gnuplot2dDataset::LINES_POINTS); 139 | Gnuplot plot(m_conf.prefix_file_name + "." + m_conf.graph_output); 140 | plot.SetTitle("TCP Congestion Window"); 141 | plot.SetTerminal(m_conf.graph_output); 142 | plot.SetLegend("Time (ms)", "Congestion Window (segments)"); 143 | plot.SetExtra( 144 | "set object 1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb \"white\" behind"); 145 | 146 | for (const auto& [nodeId, graphDataVector] : m_senderGraphData) 147 | { 148 | Gnuplot2dDataset cwndDataset; 149 | cwndDataset.SetTitle("Node " + std::to_string(nodeId) + " Cwnd"); 150 | Gnuplot2dDataset ssthreshDataset; 151 | ssthreshDataset.SetTitle("Node " + std::to_string(nodeId) + " SsThresh"); 152 | for (const auto& [time, cwnd, ssthresh] : graphDataVector) 153 | { 154 | cwndDataset.Add(time, cwnd / m_conf.adu_bytes); 155 | ssthreshDataset.Add(time, ssthresh / m_conf.adu_bytes); 156 | } 157 | plot.AddDataset(cwndDataset); 158 | plot.AddDataset(ssthreshDataset); 159 | } 160 | 161 | if (!m_receiverGraphData.empty()) 162 | { 163 | Gnuplot2dDataset receiverDataset; 164 | receiverDataset.SetTitle("Queue Size"); 165 | for (const auto& [time, queueSize] : m_receiverGraphData) 166 | { 167 | receiverDataset.Add(time, queueSize); 168 | } 169 | plot.AddDataset(receiverDataset); 170 | } 171 | 172 | std::ofstream plotFile(m_conf.prefix_file_name + ".plt"); 173 | plot.GenerateOutput(plotFile); 174 | plotFile.close(); 175 | } 176 | -------------------------------------------------------------------------------- /simulation/simulator-helper.cc: -------------------------------------------------------------------------------- 1 | #include "simulator-helper.h" 2 | 3 | NS_LOG_COMPONENT_DEFINE("SimulatorHelper"); 4 | 5 | SimulatorHelper::SimulatorHelper(const Configuration& conf, Tracer& tracer) 6 | : m_port(9), 7 | m_conf(conf), 8 | m_isInitialized(false), 9 | m_tracer(tracer) 10 | { 11 | m_ipv4Helper.SetBase("10.0.1.0", "255.255.255.0"); 12 | } 13 | 14 | void 15 | SimulatorHelper::Setup() 16 | { 17 | SetupNodes(); 18 | SetupSenderChannel(); 19 | SetupReceiverChannel(); 20 | 21 | NS_LOG_INFO("Initialize Global Routing."); 22 | Ipv4GlobalRoutingHelper::PopulateRoutingTables(); 23 | 24 | SetupSenderApplications(); 25 | SetupReceiverApplications(); 26 | SetupTracing(); 27 | 28 | m_isInitialized = true; 29 | } 30 | 31 | void 32 | SimulatorHelper::Run() 33 | { 34 | NS_LOG_FUNCTION(this); 35 | 36 | if (!m_isInitialized) 37 | { 38 | NS_LOG_WARN("SimulatorHelper is not initialized"); 39 | return; 40 | } 41 | 42 | NS_LOG_INFO("Running simulation"); 43 | Simulator::Stop(Seconds(m_conf.duration)); 44 | Simulator::Run(); 45 | Simulator::Destroy(); 46 | } 47 | 48 | void 49 | SimulatorHelper::SetupNodes() 50 | { 51 | NS_LOG_FUNCTION(this); 52 | 53 | NS_LOG_INFO("Create nodes"); 54 | m_senders.Create(m_conf.n_tcp_tahoe + m_conf.n_tcp_reno); 55 | m_receivers.Create(1); 56 | m_gateway.Create(1); 57 | 58 | InternetStackHelper internet; 59 | internet.InstallAll(); 60 | } 61 | 62 | void 63 | SimulatorHelper::SetupSenderChannel() 64 | { 65 | NS_LOG_FUNCTION(this); 66 | 67 | NS_LOG_INFO("Create sender channel"); 68 | m_s_pointToPoint.SetDeviceAttribute("DataRate", StringValue(m_conf.s_bandwidth)); 69 | m_s_pointToPoint.SetChannelAttribute("Delay", StringValue(m_conf.s_delay)); 70 | for (uint32_t i = 0; i < m_senders.GetN(); i++) 71 | { 72 | NetDeviceContainer devices = m_s_pointToPoint.Install(m_senders.Get(i), m_gateway.Get(0)); 73 | m_ipv4Helper.NewNetwork(); 74 | m_ipv4Helper.Assign(devices); 75 | } 76 | 77 | for (uint32_t i = 0; i < m_conf.n_tcp_tahoe; i++) 78 | { 79 | Config::Set("/NodeList/" + std::to_string(i) + "/$ns3::TcpL4Protocol/SocketType", 80 | TypeIdValue(TcpTahoe::GetTypeId())); 81 | Config::Set("/NodeList/" + std::to_string(i) + "/$ns3::TcpL4Protocol/RecoveryType", 82 | TypeIdValue(TcpTahoeLossRecovery::GetTypeId())); 83 | } 84 | for (uint32_t i = m_conf.n_tcp_tahoe; i < m_conf.n_tcp_tahoe + m_conf.n_tcp_reno; i++) 85 | { 86 | Config::Set("/NodeList/" + std::to_string(i) + "/$ns3::TcpL4Protocol/SocketType", 87 | TypeIdValue(TypeId::LookupByName("ns3::TcpLinuxReno"))); 88 | Config::Set("/NodeList/" + std::to_string(i) + "/$ns3::TcpL4Protocol/RecoveryType", 89 | TypeIdValue(TypeId::LookupByName("ns3::TcpClassicRecovery"))); 90 | } 91 | } 92 | 93 | void 94 | SimulatorHelper::SetupReceiverChannel() 95 | { 96 | NS_LOG_FUNCTION(this); 97 | 98 | NS_LOG_LOGIC("Create error model"); 99 | Ptr uv = CreateObject(); 100 | Ptr error_model = CreateObject(); 101 | error_model->SetRandomVariable(uv); 102 | error_model->SetUnit(RateErrorModel::ERROR_UNIT_PACKET); 103 | error_model->SetRate(m_conf.error_p); 104 | 105 | NS_LOG_INFO("Create receiver channel"); 106 | m_r_pointToPoint.SetDeviceAttribute("DataRate", StringValue(m_conf.r_bandwidth)); 107 | m_r_pointToPoint.SetChannelAttribute("Delay", StringValue(m_conf.r_delay)); 108 | m_r_pointToPoint.SetDeviceAttribute("ReceiveErrorModel", PointerValue(error_model)); 109 | 110 | NetDeviceContainer devices = m_r_pointToPoint.Install(m_gateway.Get(0), m_receivers.Get(0)); 111 | m_ipv4Helper.NewNetwork(); 112 | m_ipv4Helper.Assign(devices); 113 | 114 | TrafficControlHelper tch; 115 | tch.SetRootQueueDisc("ns3::RedQueueDisc"); 116 | tch.Uninstall(devices); 117 | QueueDiscContainer qDiscs = tch.Install(devices); 118 | qDiscs.Get(0)->TraceConnectWithoutContext("PacketsInQueue", 119 | MakeCallback(&Tracer::TcpQueueTracer, &m_tracer)); 120 | } 121 | 122 | void 123 | SimulatorHelper::SetupSenderApplications() 124 | { 125 | NS_LOG_FUNCTION(this); 126 | 127 | NS_LOG_INFO("Create sender applications"); 128 | Ipv4Address remoteAddress = m_receivers.Get(0)->GetObject()->GetAddress(1, 0).GetLocal(); 129 | BulkSendHelper source("ns3::TcpSocketFactory", InetSocketAddress(remoteAddress, m_port)); 130 | source.SetAttribute("SendSize", UintegerValue(m_conf.adu_bytes)); 131 | source.SetAttribute("MaxBytes", UintegerValue(m_conf.max_mbytes_to_send * 1000000)); 132 | source.SetAttribute("StartTime", TimeValue(Seconds(0))); 133 | source.SetAttribute("StopTime", TimeValue(Seconds(m_conf.duration))); 134 | 135 | ApplicationContainer sourceApps = source.Install(m_senders); 136 | } 137 | 138 | void 139 | SimulatorHelper::SetupReceiverApplications() 140 | { 141 | NS_LOG_FUNCTION(this); 142 | 143 | NS_LOG_INFO("Create receiver applications"); 144 | PacketSinkHelper sink("ns3::TcpSocketFactory", 145 | InetSocketAddress(Ipv4Address::GetAny(), m_port)); 146 | sink.SetAttribute("StartTime", TimeValue(Seconds(0))); 147 | sink.SetAttribute("StopTime", TimeValue(Seconds(m_conf.duration))); 148 | ApplicationContainer sinkApps = sink.Install(m_receivers); 149 | } 150 | 151 | void 152 | SimulatorHelper::SetupTracing() 153 | { 154 | Simulator::Schedule(NanoSeconds(1), MakeCallback(&Tracer::ScheduleTracing, &m_tracer)); 155 | Simulator::ScheduleDestroy(MakeCallback(&Tracer::PrintGraphDataToFile, &m_tracer)); 156 | 157 | // Set up tracing if enabled 158 | if (m_conf.ascii_tracing) 159 | { 160 | AsciiTraceHelper ascii; 161 | m_s_pointToPoint.EnableAsciiAll(ascii.CreateFileStream(m_conf.prefix_file_name + ".tr")); 162 | m_r_pointToPoint.EnableAsciiAll(ascii.CreateFileStream(m_conf.prefix_file_name + ".tr")); 163 | } 164 | if (m_conf.pcap_tracing) 165 | { 166 | m_s_pointToPoint.EnablePcapAll(m_conf.prefix_file_name, false); 167 | m_r_pointToPoint.EnablePcapAll(m_conf.prefix_file_name, false); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TCP comparison 2 | 3 | This is a comparison between two old TCP variants: TCP Tahoe and TCP Reno, using the ns-3 simulator. 4 | 5 | ## TCP variants 6 | 7 | Over the years, many TCP variants have been developed, each trying to improve the performance of the predecessor, given the right conditions. 8 | Most of them have become highly sophisticated. 9 | Still, it can be interesting to look at two of the first TCP variants: TCP Tahoe and TCP Reno and see the basic components of any TCP variant. 10 | 11 | ### TCP Tahoe 12 | 13 | TCP Tahoe is the first TCP variant to consider the issue of congestion. 14 | It's mechanism is very simple and can be easily understood. 15 | This makes it a good starting point to understand how TCP works. 16 | It uses 17 | 18 | - **slow start**: it doubles the congestion window every RTT. 19 | - **congestion avoidance**: it increases the congestion window by 1 every RTT. 20 | - **fast retransmit**: it detects packet loss by checking if the next packet is received. 21 | 22 | ### TCP Reno 23 | 24 | TCP Reno is the first TCP variant to be standardized. It is a newer TCP variant than TCP Tahoe and uses a more complex mechanism to handle congestion. 25 | Apart from the slow start and congestion avoidance mechanisms, it uses: 26 | 27 | - **fast recovery**: it recovers from packet loss by halving the congestion window instead of going straight in the slow start phase. 28 | 29 | ## Installation 30 | 31 | ### Requirements 32 | 33 | - [ns-3 simulator v3.37](https://www.nsnam.org/). 34 | - [c++ compiler](https://gcc.gnu.org/). 35 | - [cmake](https://cmake.org/). 36 | - [python3](https://www.python.org/). 37 | 38 | The simulation uses the [ns-3 simulator](https://www.nsnam.org/), and has been developed and tested with the version 3.37. 39 | 40 | ### Build 41 | 42 | First, make sure you have already completed all the step required for the installation of the simulator shown [here](https://www.nsnam.org/docs/release/3.37/tutorial/html/index.html). 43 | The directory structure should look something like this: 44 | 45 | ```bash 46 | . 47 | └── ns-allinone-3.37/ 48 | └── ns-3.37/ 49 | ├── ns3 50 | ├── examples/ 51 | ├── src/ 52 | ├── scratch/ 53 | └── ... 54 | ``` 55 | 56 | Move to the `scratch` folder and clone the repository: 57 | 58 | ```bash 59 | cd ns-allinone-3.37/ns-3.37/scratch 60 | git clone git@github.com:TendTo/ns3-Tahoe-vs-Reno.git 61 | ``` 62 | 63 | Lastly, move back to the `ns-3.37` folder and build the simulation: 64 | 65 | ```bash 66 | cd .. 67 | ./ns3 run "p2p-project --PrintHelp" 68 | ``` 69 | 70 | ## Usage 71 | 72 | The simulation is highly configurable. The following options are available: 73 | 74 | ```bash 75 | Usage: ./ns3 run "p2p-project [options]" 76 | 77 | Program Options: 78 | --n_tcp_tahoe: Number of Tcp Tahoe nodes [1] 79 | --n_tcp_reno: Number of Tcp Reno nodes [1] 80 | --s_buf_size: Sender buffer size (bytes) [131072] 81 | --r_buf_size: Receiver buffer size (bytes) [131072] 82 | --cwnd: Initial congestion window (segments) [1] 83 | --ssthresh: Initial slow start threshold (segments) [65535] 84 | --mtu: Size of IP packets to send (bytes) [1500] 85 | --sack: Enable SACK [true] 86 | --nagle: Enable Nagle algorithm [false] 87 | --error_p: Packet error rate [0] 88 | --s_bandwidth: Sender link bandwidth [10Mbps] 89 | --s_delay: Sender link delay [40ms] 90 | --r_bandwidth: Receiver link bandwidth [10Mbps] 91 | --r_delay: Receiver link delay [40ms] 92 | --tcp_queue_size: TCP queue size (packets) [25] 93 | --run: Run id [0] 94 | --duration: Duration of the simulation (s) [3] 95 | --max_mbytes_to_send: Maximum number of megabytes to send (MB) [0] 96 | --prefix_file_name: Prefix file name [P2P-project] 97 | --graph_output: The type of image to output: png, svg [png] 98 | --ascii_tracing: Enable ASCII tracing [false] 99 | --pcap_tracing: Enable Pcap tracing [false] 100 | 101 | General Arguments: 102 | --PrintGlobals: Print the list of globals. 103 | --PrintGroups: Print the list of groups. 104 | --PrintGroup=[group]: Print all TypeIds of group. 105 | --PrintTypeIds: Print all TypeIds. 106 | --PrintAttributes=[typeid]: Print all attributes of typeid. 107 | --PrintVersion: Print the ns-3 version. 108 | --PrintHelp: Print this help message. 109 | ``` 110 | 111 | ## Example usages 112 | 113 | The following are some example usages of the simulation with the output graphs. 114 | 115 | ### Scenario 1 116 | 117 | ```bash 118 | ./ns3 run "p2p-project --n_tcp_tahoe=1 --n_tcp_reno=0 --error_p=0.001 --run=0 --duration=10" 119 | ``` 120 | 121 | ```mermaid 122 | flowchart LR 123 | r{{router}} 124 | n0[Node 0\nTCP Tahoe] 125 | i((Receiver)) 126 | 127 | r <--Random loss--> i 128 | n0 <----> r 129 | ``` 130 | 131 | ![Graph 1](./docs/img/graph-1.png) 132 | 133 | ### Scenario 2 134 | 135 | ```bash 136 | ./ns3 run "p2p-project --n_tcp_tahoe=0 --n_tcp_reno=1 --error_p=0.001 --run=0 --duration=10" 137 | ``` 138 | 139 | ```mermaid 140 | flowchart LR 141 | r{{router}} 142 | n0[Node 0\nTCP Reno] 143 | i((Receiver)) 144 | 145 | r <--Random loss--> i 146 | n0 <----> r 147 | ``` 148 | 149 | ![Graph 2](./docs/img/graph-2.png) 150 | 151 | ### Scenario 3 152 | 153 | ```bash 154 | ./ns3 run "p2p-project --n_tcp_tahoe=1 --n_tcp_reno=1 --error_p=0.002 --run=1 --duration=10" 155 | ``` 156 | 157 | ```mermaid 158 | flowchart LR 159 | r{{router}} 160 | n0[Node 0\nTCP Tahoe] 161 | n1[Node 1\nTCP Reno] 162 | i((Receiver)) 163 | 164 | r <--Random loss--> i 165 | n0 <----> r 166 | n1 <----> r 167 | ``` 168 | 169 | ![Graph 3](./docs/img/graph-3.png) 170 | 171 | ### Scenario 4 172 | 173 | ```bash 174 | ./ns3 run "p2p-project --n_tcp_tahoe=2 --n_tcp_reno=2 --run=0 --duration=10" 175 | ``` 176 | 177 | ```mermaid 178 | flowchart LR 179 | r{{router}} 180 | n0[Node 0\nTCP Tahoe] 181 | n1[Node 1\nTCP Tahoe] 182 | n2[Node 2\nTCP Reno] 183 | n3[Node 3\nTCP Reno] 184 | i((Receiver)) 185 | 186 | r <----> i 187 | n0 <----> r 188 | n1 <----> r 189 | n2 <----> r 190 | n3 <----> r 191 | ``` 192 | 193 | ![Graph 4](./docs/img/graph-4.png) 194 | 195 | ## Conclusion 196 | 197 | TCP Tahoe and TCP Reno are very similar. The main difference is that TCP Reno uses a fast retransmit mechanism to detect packet loss and a fast recovery mechanism to recover from packet loss and follows up with a fast recovery phase. 198 | This means that, in most cases, TCP Reno outperforms TCP Tahoe. 199 | 200 | ## References 201 | 202 | - [ns-3](https://www.nsnam.org/) 203 | - [TCP congestion control](https://datatracker.ietf.org/doc/html/rfc5681) 204 | - [A Comparative Analysis of TCP Tahoe, Reno, New-Reno, SACK and Vegas](https://inst.eecs.berkeley.edu/~ee122/fa05/projects/Project2/SACKRENEVEGAS.pdf) 205 | --------------------------------------------------------------------------------