Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Fix build with ns3 v3.36+
[simgrid.git] / src / kernel / resource / models / network_ns3.cpp
index eded3ed..3d4c065 100644 (file)
@@ -27,8 +27,8 @@
 #include <ns3/packet-sink-helper.h>
 #include <ns3/point-to-point-helper.h>
 
-#include "ns3/mobility-module.h"
-#include "ns3/wifi-module.h"
+#include <ns3/mobility-module.h>
+#include <ns3/wifi-module.h>
 
 #include "network_ns3.hpp"
 #include "src/kernel/resource/models/ns3/ns3_simulator.hpp"
@@ -50,7 +50,6 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(res_ns3, res_network, "Network model based on ns
  *****************/
 
 extern std::map<std::string, SgFlow*, std::less<>> flow_from_sock;
-extern std::map<std::string, ns3::ApplicationContainer, std::less<>> sink_from_sock;
 
 static int number_of_links    = 1;
 static int number_of_networks = 1;
@@ -120,8 +119,14 @@ static void zoneCreation_cb(simgrid::s4u::NetZone const& zone)
   wifiPhy.Set("Antennas", ns3::UintegerValue(nss_value));
   wifiPhy.Set("MaxSupportedTxSpatialStreams", ns3::UintegerValue(nss_value));
   wifiPhy.Set("MaxSupportedRxSpatialStreams", ns3::UintegerValue(nss_value));
-#if NS3_MINOR_VERSION > 33
+#if NS3_MINOR_VERSION < 33
+  // This fails with "The channel width does not uniquely identify an operating channel" on v3.34,
+  // so we specified the ChannelWidth of wifiPhy to 40, above, when creating wifiPhy with v3.34 and higher
+  ns3::Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/ChannelWidth", ns3::UintegerValue(40));
+#elif NS3_MINOR_VERSION < 36
   wifiPhy.Set("ChannelWidth", ns3::UintegerValue(40));
+#else
+  wifiPhy.Set("ChannelSettings", ns3::StringValue("{0, 40, BAND_UNSPECIFIED, 0}"));
 #endif
   wifiMac.SetType("ns3::ApWifiMac", "Ssid", ns3::SsidValue(ssid));
 
@@ -167,12 +172,6 @@ static void zoneCreation_cb(simgrid::s4u::NetZone const& zone)
     ns3::Simulator::Schedule(ns3::Seconds(start_time_value), &resumeWifiDevice, device);
   }
 
-#if NS3_MINOR_VERSION < 33
-  // This fails with "The channel width does not uniquely identify an operating channel" on v3.34,
-  // so we specified the ChannelWidth of wifiPhy to 40, above, when creating wifiPhy with v3.34 and higher
-  ns3::Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/ChannelWidth", ns3::UintegerValue(40));
-#endif
-
   mobility.SetPositionAllocator(positionAllocS);
   mobility.Install(nodes);
   ns3::Ipv4AddressHelper address;
@@ -288,8 +287,9 @@ static void XBT_ATTRIB_CONSTRUCTOR(800) simgrid_ns3_network_model_register()
       });
 }
 
-static simgrid::config::Flag<std::string>
-    ns3_tcp_model("ns3/TcpModel", "The ns-3 tcp model can be: NewReno or Reno or Tahoe", "default");
+static simgrid::config::Flag<std::string> ns3_network_model_name("ns3/NetworkModel", {"ns3/TcpModel"},
+                                                                 "The ns-3 tcp model can be: NewReno or Cubic",
+                                                                 "default", [](const std::string&) {});
 static simgrid::config::Flag<std::string> ns3_seed(
     "ns3/seed",
     "The random seed provided to ns-3. Either 'time' to seed with time(), blank to not set (default), or a number.", "",
@@ -317,20 +317,34 @@ NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name)
              "LinkEnergy plugin and ns-3 network models are not compatible. Are you looking for Ecofen, maybe?");
 
   NetPointNs3::EXTENSION_ID = routing::NetPoint::extension_create<NetPointNs3>();
+  auto const& NetworkProtocol = ns3_network_model_name.get();
+
+  if (NetworkProtocol == "UDP") {
+    /*UdpClient=0
+UdpEchoClientApplication=0
+UdpEchoServerApplication=0
+UdpL4Protocol=0
+UdpServer=0
+UdpSocket=0
+UdpSocketImpl=0
+UdpTraceClient=0*/
+    LogComponentEnable("UdpSocket", ns3::LOG_LEVEL_DEBUG);
+    LogComponentEnable("UdpL4Protocol", ns3::LOG_LEVEL_DEBUG);
+  } else {
+    ns3::Config::SetDefault("ns3::TcpSocket::SegmentSize", ns3::UintegerValue(1000));
+    ns3::Config::SetDefault("ns3::TcpSocket::DelAckCount", ns3::UintegerValue(1));
+    ns3::Config::SetDefault("ns3::TcpSocketBase::Timestamp", ns3::BooleanValue(false));
+  }
 
-  ns3::Config::SetDefault("ns3::TcpSocket::SegmentSize", ns3::UintegerValue(1000));
-  ns3::Config::SetDefault("ns3::TcpSocket::DelAckCount", ns3::UintegerValue(1));
-  ns3::Config::SetDefault("ns3::TcpSocketBase::Timestamp", ns3::BooleanValue(false));
-
-  if (auto const& TcpProtocol = ns3_tcp_model.get(); TcpProtocol == "default") {
-    /* nothing to do */
+  if (NetworkProtocol == "NewReno" || NetworkProtocol == "Cubic") {
+    XBT_INFO("Switching Tcp protocol to '%s'", NetworkProtocol.c_str());
+    ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", ns3::StringValue("ns3::Tcp" + NetworkProtocol));
 
-  } else if (TcpProtocol == "Reno" || TcpProtocol == "NewReno" || TcpProtocol == "Tahoe") {
-    XBT_INFO("Switching Tcp protocol to '%s'", TcpProtocol.c_str());
-    ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", ns3::StringValue("ns3::Tcp" + TcpProtocol));
+  } else if (NetworkProtocol == "UDP") {
+    XBT_INFO("Switching network protocol to UDP.");
 
-  } else {
-    xbt_die("The ns3/TcpModel must be: NewReno or Reno or Tahoe");
+  } else if (NetworkProtocol != "default") {
+    xbt_die("The ns3/NetworkModel must be: NewReno, Cubic or UDP but it's '%s'", NetworkProtocol.c_str());
   }
 
   routing::NetPoint::on_creation.connect([](routing::NetPoint& pt) {
@@ -340,7 +354,6 @@ NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name)
 
   s4u::Engine::on_platform_created_cb([]() {
     /* Create the ns3 topology based on routing strategy */
-    ns3::GlobalRouteManager::DeleteGlobalRoutes(); // just in case this callback is called twice
     ns3::GlobalRouteManager::BuildGlobalRoutingDatabase();
     ns3::GlobalRouteManager::InitializeRoutes();
   });
@@ -377,6 +390,31 @@ Action* NetworkNS3Model::communicate(s4u::Host* src, s4u::Host* dst, double size
   return new NetworkNS3Action(this, size, src, dst);
 }
 
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+/* If patched, ns3 is idempotent and nice to use */
+bool NetworkNS3Model::next_occurring_event_is_idempotent()
+{
+  return true;
+}
+
+double NetworkNS3Model::next_occurring_event(double sg_time)
+{
+  if (get_started_action_set()->empty()) {
+    return -1.0;
+  }
+
+  double ns3_time = ns3::Simulator::GetNextEventTime().GetSeconds();
+  XBT_DEBUG("NS3 tells that the next occuring event is at %f (it's %f in SimGrid), so NS3 returns a delta of %f.",
+            ns3_time, sg_time, ns3_time - sg_time);
+  return ns3_time - sg_time;
+}
+#else
+/* NS3 is only idempotent with the appropriate patch */
+bool NetworkNS3Model::next_occurring_event_is_idempotent()
+{
+  return false;
+}
+
 double NetworkNS3Model::next_occurring_event(double now)
 {
   double time_to_next_flow_completion = 0.0;
@@ -406,11 +444,25 @@ double NetworkNS3Model::next_occurring_event(double now)
 
   return time_to_next_flow_completion;
 }
+#endif
 
 void NetworkNS3Model::update_actions_state(double now, double delta)
 {
   static std::vector<std::string> socket_to_destroy;
 
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+  /* If the ns-3 model is idempotent, it won't get updated in next_occurring_event() */
+
+  if (delta >= 0) {
+    XBT_DEBUG("DO START simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta,
+              simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+    ns3_simulator(delta);
+  } else {
+    XBT_DEBUG("don't start simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta,
+              simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+  }
+#endif
+
   for (const auto& [ns3_socket, sgFlow] : flow_from_sock) {
     NetworkNS3Action* action = sgFlow->action_;
     XBT_DEBUG("Processing flow %p (socket %s, action %p)", sgFlow, ns3_socket.c_str(), action);
@@ -427,7 +479,7 @@ void NetworkNS3Model::update_actions_state(double now, double delta)
 
       std::vector<StandardLinkImpl*> route;
       action->get_src().route_to(&action->get_dst(), route, nullptr);
-      for (auto const& link : route)
+      for (auto const* link : route)
         instr::resource_set_utilization("LINK", "bandwidth_used", link->get_cname(), action->get_category(),
                                         data_delta_sent / delta, now - delta, delta);
 
@@ -455,7 +507,6 @@ void NetworkNS3Model::update_actions_state(double now, double delta)
     }
     delete flow;
     flow_from_sock.erase(ns3_socket);
-    sink_from_sock.erase(ns3_socket);
   }
 }
 
@@ -506,7 +557,7 @@ NetworkNS3Action::NetworkNS3Action(Model* model, double totalBytes, s4u::Host* s
   if (src == dst) {
     if (static bool warned = false; not warned) {
       XBT_WARN("Sending from a host %s to itself is not supported by ns-3. Every such communication finishes "
-               "immediately upon startup.",
+               "immediately upon startup in the SimGrid+ns-3 system.",
                src->get_cname());
       warned = true;
     }
@@ -533,17 +584,18 @@ NetworkNS3Action::NetworkNS3Action(Model* model, double totalBytes, s4u::Host* s
              dst->get_netpoint()->get_cname());
 
   ns3::PacketSinkHelper sink("ns3::TcpSocketFactory", ns3::InetSocketAddress(ns3::Ipv4Address::GetAny(), port_number));
-  ns3::ApplicationContainer apps = sink.Install(dst_node);
+  sink.Install(dst_node);
 
   ns3::Ptr<ns3::Socket> sock = ns3::Socket::CreateSocket(src_node, ns3::TcpSocketFactory::GetTypeId());
 
-  XBT_DEBUG("Create socket %s for a flow of %.0f Bytes from %s to %s with Interface %s",
-            transform_socket_ptr(sock).c_str(), totalBytes, src->get_cname(), dst->get_cname(), addr.c_str());
+  auto sock_addr = transform_socket_ptr(sock);
+  XBT_DEBUG("Create socket %s for a flow of %.0f Bytes from %s to %s with Interface %s", sock_addr.c_str(), totalBytes,
+            src->get_cname(), dst->get_cname(), addr.c_str());
 
-  flow_from_sock.try_emplace(transform_socket_ptr(sock), new SgFlow(static_cast<uint32_t>(totalBytes), this));
-  sink_from_sock.try_emplace(transform_socket_ptr(sock), apps);
+  flow_from_sock.try_emplace(sock_addr, new SgFlow(static_cast<uint32_t>(totalBytes), this));
 
   sock->Bind(ns3::InetSocketAddress(port_number));
+
   ns3::Simulator::ScheduleNow(&start_flow, sock, addr.c_str(), port_number);
 
   port_number = 1 + (port_number % UINT16_MAX);
@@ -574,22 +626,27 @@ void NetworkNS3Action::update_remains_lazy(double /*now*/)
 
 ns3::Ptr<ns3::Node> get_ns3node_from_sghost(const simgrid::s4u::Host* host)
 {
-  xbt_assert(host->get_netpoint()->extension<NetPointNs3>() != nullptr, "Please only use this function on ns-3 nodes");
-  return host->get_netpoint()->extension<NetPointNs3>()->ns3_node_;
+  auto* netext = host->get_netpoint()->extension<NetPointNs3>();
+  xbt_assert(netext != nullptr, "Please only use this function on ns-3 nodes");
+  return netext->ns3_node_;
 }
 } // namespace simgrid
 
-void ns3_simulator(double maxSeconds)
+void ns3_simulator(double maxSeconds) // maxSecond is a delay, not an absolute time
 {
   ns3::EventId id;
-  if (maxSeconds > 0.0) // If there is a maximum amount of time to run
+  if (maxSeconds >= 0.0) // If there is a maximum amount of time to run
     id = ns3::Simulator::Schedule(ns3::Seconds(maxSeconds), &ns3::Simulator::Stop);
 
-  XBT_DEBUG("Start simulator for at most %fs (current time: %f)", maxSeconds, simgrid::kernel::EngineImpl::get_clock());
+  XBT_DEBUG("Start simulator for at most %fs (current simgrid time: %f; current ns3 time: %f)", maxSeconds,
+            simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+  xbt_assert(maxSeconds >= 0.0);
+#endif
   ns3::Simulator::Run();
-  XBT_DEBUG("Simulator stopped at %fs", ns3::Simulator::Now().GetSeconds());
+  XBT_DEBUG("ns3 simulator stopped at %fs", ns3::Simulator::Now().GetSeconds());
 
-  if (maxSeconds > 0.0)
+  if (maxSeconds >= 0.0)
     id.Cancel();
 }