From 8280159652ffc4051f84145e81e16a09a1a48189 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Sun, 2 Apr 2023 13:15:28 -0300 Subject: [PATCH 01/10] Buffer timeout with msec resolution. --- CMakeLists.txt | 2 +- model/ofswitch13-device.cc | 12 +++++------- model/ofswitch13-device.h | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7953c49..11d326a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ if(NOT bofuss_FOUND AND NOT ${bofuss_FOUND}) ExternalProject_Add( bofuss_dep GIT_REPOSITORY https://github.com/ljerezchaves/ofsoftswitch13.git - GIT_TAG 0901aeb7f1612016e43e774381507c2aaef4afc2 + GIT_TAG 625f01384522579db8f1c361209319530d53774a PREFIX bofuss_dep BUILD_IN_SOURCE TRUE UPDATE_DISCONNECTED TRUE diff --git a/model/ofswitch13-device.cc b/model/ofswitch13-device.cc index 0394d13..b821be9 100644 --- a/model/ofswitch13-device.cc +++ b/model/ofswitch13-device.cc @@ -653,7 +653,7 @@ OFSwitch13Device::PacketDestroyCallback(struct packet* pkt) } void -OFSwitch13Device::BufferSaveCallback(struct packet* pkt, time_t timeout) +OFSwitch13Device::BufferSaveCallback(struct packet* pkt, uint64_t timeout) { Ptr dev = OFSwitch13Device::GetDevice(pkt->dp->id); dev->BufferPacketSave(pkt->ns3_uid, timeout); @@ -1310,7 +1310,7 @@ OFSwitch13Device::NotifyPacketDroppedByTable(struct packet* pkt, } void -OFSwitch13Device::BufferPacketSave(uint64_t packetId, time_t timeout) +OFSwitch13Device::BufferPacketSave(uint64_t packetId, uint64_t timeout) { NS_LOG_FUNCTION(this << packetId); @@ -1331,10 +1331,8 @@ OFSwitch13Device::BufferPacketSave(uint64_t packetId, time_t timeout) m_pipePkt.DelCopy(packetId); NS_ASSERT_MSG(!m_pipePkt.IsValid(), "Packet copy still in pipeline."); - // Scheduling the buffer remove for expired packet. Since packet timeout - // resolution is expressed in seconds, let's double it to avoid rounding - // conflicts. - Simulator::Schedule(Time::FromInteger(2 * timeout, Time::S), + // Scheduling the buffer removal for expired packet. + Simulator::Schedule(Time::FromInteger(timeout, Time::MS), &OFSwitch13Device::BufferPacketDelete, this, packetId); @@ -1349,7 +1347,7 @@ OFSwitch13Device::BufferPacketRetrieve(uint64_t packetId) // Find packet in buffer. auto it = m_bufferPkts.find(packetId); - NS_ASSERT_MSG(it != m_bufferPkts.end(), "Packet not found in buffer."); + NS_ABORT_MSG_IF(it == m_bufferPkts.end(), "Packet not found in buffer."); // Save packet into pipeline structure. m_pipePkt.SetPacket(it->first, it->second); diff --git a/model/ofswitch13-device.h b/model/ofswitch13-device.h index ad7051a..4c46917 100644 --- a/model/ofswitch13-device.h +++ b/model/ofswitch13-device.h @@ -330,7 +330,7 @@ class OFSwitch13Device : public Object * \param pkt The internal packet saved into buffer. * \param timeout The timeout for this packet into buffer. */ - static void BufferSaveCallback(struct packet* pkt, time_t timeout); + static void BufferSaveCallback(struct packet* pkt, uint64_t timeout); /** * Callback fired when a packet is retrieved from buffer. @@ -533,7 +533,7 @@ class OFSwitch13Device : public Object * the ns-3 packet in pipeline and save into buffer map. \param packetId The * ns-3 packet id. \param timeout The buffer timeout. */ - void BufferPacketSave(uint64_t packetId, time_t timeout); + void BufferPacketSave(uint64_t packetId, uint64_t timeout); /** * Notify this device of a packet retrieved from buffer. This method will From e3bc8948936fe4170ccdc946c2bd704223c8158a Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Sun, 2 Apr 2023 15:31:43 -0300 Subject: [PATCH 02/10] Coding style and typo fixes. --- RELEASE_NOTES | 22 +- doc/source/ofswitch13-usage.rst | 132 +++---- examples/ofswitch13-custom-switch.cc | 6 +- examples/ofswitch13-external-controller.cc | 6 +- examples/ofswitch13-first.cc | 6 +- .../ofswitch13-logical-port/gtp-tunnel-app.cc | 18 +- .../ofswitch13-logical-port/gtp-tunnel-app.h | 10 +- examples/ofswitch13-logical-port/main.cc | 8 +- .../tunnel-controller.cc | 27 +- examples/ofswitch13-multiple-controllers.cc | 6 +- examples/ofswitch13-multiple-domains.cc | 6 +- examples/ofswitch13-qos-controller/main.cc | 5 +- .../qos-controller.cc | 2 +- .../qos-controller.h | 2 +- examples/ofswitch13-single-domain.cc | 6 +- helper/ofswitch13-device-container.cc | 5 +- helper/ofswitch13-device-container.h | 66 ++-- helper/ofswitch13-external-helper.cc | 32 +- helper/ofswitch13-external-helper.h | 1 - helper/ofswitch13-helper.cc | 45 +-- helper/ofswitch13-helper.h | 54 +-- helper/ofswitch13-internal-helper.cc | 62 +--- helper/ofswitch13-internal-helper.h | 5 +- helper/ofswitch13-stats-calculator.cc | 156 ++++---- helper/ofswitch13-stats-calculator.h | 8 +- model/ofswitch13-controller.cc | 88 ++--- model/ofswitch13-controller.h | 78 ++-- model/ofswitch13-device.cc | 343 +++++++----------- model/ofswitch13-device.h | 120 +++--- model/ofswitch13-interface.cc | 13 +- model/ofswitch13-interface.h | 27 +- model/ofswitch13-learning-controller.cc | 43 +-- model/ofswitch13-learning-controller.h | 4 +- model/ofswitch13-port.cc | 69 ++-- model/ofswitch13-port.h | 28 +- model/ofswitch13-priority-queue.cc | 19 +- model/ofswitch13-priority-queue.h | 6 +- model/ofswitch13-queue.cc | 42 +-- model/ofswitch13-queue.h | 19 +- model/ofswitch13-socket-handler.cc | 2 + model/ofswitch13-socket-handler.h | 12 +- 41 files changed, 696 insertions(+), 913 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 03e8f1d..d835fe9 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -23,7 +23,7 @@ Release 5.2.0 (Mar 31, 2023) - Updating CMakeLists.txt to automatically download and build the BOFUSS library. -- Updating the module documentation to reflext the changes in library building. +- Updating the module documentation to reflect the changes in library building. - Replacing the outdated ofsoftswitch13 library name by BOFUSS in the project (source code and documentation). @@ -77,17 +77,17 @@ Release 5.0.0 (Dec 19, 2021) - BE AWARE THAT this release brings incompatible API changes in respect to prior OFSwitch13 versions. -- Refactoring OFSwitch13Controller::Dpctl* () methods: - * The DpctlExecute () method has no more overloaded definitions. The target +- Refactoring OFSwitch13Controller::Dpctl*() methods: + * The DpctlExecute() method has no more overloaded definitions. The target switch (first parameter) must be the switch's datapath IP. Previous signature using the Ptr swtch pointer was removed. - Users can fix compilation errors by just using swtch->GetDpId () when - invoking DpctlExecute (); - * The DpctlSchedule () method was deprecated in favor of DpctlExecute (). - When the switch is not connected to the controller, the DpctlExecute () + Users can fix compilation errors by just using swtch->GetDpId() when + invoking DpctlExecute(); + * The DpctlSchedule() method was deprecated in favor of DpctlExecute(). + When the switch is not connected to the controller, the DpctlExecute() will automatically schedule the command for execution just after the handshake procedure. This is particularly useful for executing dpctl - commands when creating the topology, before invoking Simulator::Run (). + commands when creating the topology, before invoking Simulator::Run(). - Creating a new OFSwitch13Device::TableDrop trace source to notify unmatched packets dropped by flow tables without table-miss entries. A new TabDrps @@ -356,7 +356,7 @@ Release 3.0.0 (Feb 10, 2017) - Removing the controller address attribute from the OpenFlow device. It now must be indicated by the helper as a parameter to the - StartControllerConnection () device method. + StartControllerConnection() device method. - Creating the OFSwitch13StatsCalculator for switch performance monitoring. @@ -372,7 +372,7 @@ Release 3.0.0 (Feb 10, 2017) - Refactoring the OFSwitch13Controller class: * Splitting the DpctlCommand into 'execute' and 'schedule' versions; * Removing dependence from OFSwitch13Device class; - * Renaming ConnectionStarted () method to HandshakeSuccessful () to reflect + * Renaming ConnectionStarted() method to HandshakeSuccessful() to reflect the method semantic; * Implementing the handshake procedure; * Implementing barrier reply, hello and features reply handlers; @@ -386,7 +386,7 @@ Release 3.0.0 (Feb 10, 2017) * Supporting the configuration of OpenFlow networks domains with multiple controllers; * Removing the dependence of installing the controller before the switches; - * Introducing the CreateOpenFlowChannels () member function to effectively + * Introducing the CreateOpenFlowChannels() member function to effectively connect switches to controllers after installing switches and controllers into respective nodes (the use of this function is mandatory). diff --git a/doc/source/ofswitch13-usage.rst b/doc/source/ofswitch13-usage.rst index 88a8035..4029723 100644 --- a/doc/source/ofswitch13-usage.rst +++ b/doc/source/ofswitch13-usage.rst @@ -93,68 +93,68 @@ This script connects two hosts to a single OpenFlow switch using CSMA links, and .. code-block:: cpp #include - #include #include + #include #include + #include #include - #include using namespace ns3; int - main (int argc, char *argv[]) + main(int argc, char* argv[]) { - // Enable checksum computations (required by OFSwitch13 module) - GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true)); + // Enable checksum computations (required by OFSwitch13 module) + GlobalValue::Bind("ChecksumEnabled", BooleanValue(true)); - // Create two host nodes - NodeContainer hosts; - hosts.Create (2); + // Create two host nodes + NodeContainer hosts; + hosts.Create(2); - // Create the switch node - Ptr switchNode = CreateObject (); + // Create the switch node + Ptr switchNode = CreateObject(); - // Use the CsmaHelper to connect the host nodes to the switch. - CsmaHelper csmaHelper; - NetDeviceContainer hostDevices; - NetDeviceContainer switchPorts; - for (size_t i = 0; i < hosts.GetN (); i++) + // Use the CsmaHelper to connect the host nodes to the switch. + CsmaHelper csmaHelper; + NetDeviceContainer hostDevices; + NetDeviceContainer switchPorts; + for (size_t i = 0; i < hosts.GetN(); i++) { - NodeContainer pair (hosts.Get (i), switchNode); - NetDeviceContainer link = csmaHelper.Install (pair); - hostDevices.Add (link.Get (0)); - switchPorts.Add (link.Get (1)); + NodeContainer pair(hosts.Get(i), switchNode); + NetDeviceContainer link = csmaHelper.Install(pair); + hostDevices.Add(link.Get(0)); + switchPorts.Add(link.Get(1)); } - // Create the controller node - Ptr controllerNode = CreateObject (); - - // Configure the OpenFlow network domain - Ptr of13Helper = CreateObject (); - of13Helper->InstallController (controllerNode); - of13Helper->InstallSwitch (switchNode, switchPorts); - of13Helper->CreateOpenFlowChannels (); - - // Install the TCP/IP stack into hosts nodes - InternetStackHelper internet; - internet.Install (hosts); - - // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; - Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase ("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign (hostDevices); - - // Configure ping application between hosts - PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); - pingHelper.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE)); - ApplicationContainer pingApps = pingHelper.Install (hosts.Get (0)); - pingApps.Start (Seconds (1)); - - // Run the simulation - Simulator::Stop (Seconds (10)); - Simulator::Run (); - Simulator::Destroy (); + // Create the controller node + Ptr controllerNode = CreateObject(); + + // Configure the OpenFlow network domain + Ptr of13Helper = CreateObject(); + of13Helper->InstallController(controllerNode); + of13Helper->InstallSwitch(switchNode, switchPorts); + of13Helper->CreateOpenFlowChannels(); + + // Install the TCP/IP stack into hosts nodes + InternetStackHelper internet; + internet.Install(hosts); + + // Set IPv4 host addresses + Ipv4AddressHelper ipv4Helper; + Ipv4InterfaceContainer hostIpIfaces; + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); + + // Configure ping application between hosts + PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); + pingHelper.SetAttribute("VerboseMode", EnumValue(Ping::VerboseMode::VERBOSE)); + ApplicationContainer pingApps = pingHelper.Install(hosts.Get(0)); + pingApps.Start(Seconds(1)); + + // Run the simulation + Simulator::Stop(Seconds(10)); + Simulator::Run(); + Simulator::Destroy(); } At first, don't forget to enable checksum computations, which are required by the |ofs13| module. @@ -369,14 +369,14 @@ The following code, based on the ``openflow-switch.cc`` example, is used for dem // Create the learning controller app Ptr controller; - controller = CreateObject (); - if (!timeout.IsZero ()) + controller = CreateObject(); + if (!timeout.IsZero()) { - controller->SetAttribute ("ExpirationTime", TimeValue (timeout)); + controller->SetAttribute("ExpirationTime", TimeValue(timeout)); } // Install the switch device, ports and set the controller - ofHelper.Install (switchNode, switchDevices, controller); + ofHelper.Install(switchNode, switchDevices, controller); // Other configurations: TCP/IP stack, apps, monitors, etc. // ... @@ -396,23 +396,23 @@ The following code implements the same logic in the |ofs13| module: // ... // Create the OpenFlow 1.3 helper - Ptr of13Helper = CreateObject (); + Ptr of13Helper = CreateObject(); // Create the controller node and install the learning controller app into it - Ptr controllerNode = CreateObject (); - of13Helper->InstallController (controllerNode); + Ptr controllerNode = CreateObject(); + of13Helper->InstallController(controllerNode); // Install the switch device and ports. - of13Helper->InstallSwitch (switchNode, switchDevices); + of13Helper->InstallSwitch(switchNode, switchDevices); // Create the OpenFlow channel connections. - of13Helper->CreateOpenFlowChannels (); + of13Helper->CreateOpenFlowChannels(); // Other configurations: TCP/IP stack, apps, monitors, etc. // ... // Arbitrary simulation duration (can be changed for any value) - Simulator::Stop (Seconds (10)); + Simulator::Stop(Seconds(10)); Note that the |ofs13| module requires a new node to install the controller application into it. The ``InstallController()`` function creates the learning application object instance and installs it in the ``controllerNode``. @@ -480,18 +480,18 @@ The experimental ``external-controller.cc`` example uses the ``OFSwitch13Externa // ... // Configure the OpenFlow network domain using an external controller - Ptr of13Helper = CreateObject (); - Ptr ctrlDev = of13Helper->InstallExternalController (controllerNode); - of13Helper->InstallSwitch (switches.Get (0), switchPorts [0]); - of13Helper->InstallSwitch (switches.Get (1), switchPorts [1]); - of13Helper->CreateOpenFlowChannels (); + Ptr of13Helper = CreateObject(); + Ptr ctrlDev = of13Helper->InstallExternalController(controllerNode); + of13Helper->InstallSwitch(switches.Get(0), switchPorts [0]); + of13Helper->InstallSwitch(switches.Get(1), switchPorts [1]); + of13Helper->CreateOpenFlowChannels(); // TapBridge the controller device to local machine // The default configuration expects a controller on local port 6653 TapBridgeHelper tapBridge; - tapBridge.SetAttribute ("Mode", StringValue ("ConfigureLocal")); - tapBridge.SetAttribute ("DeviceName", StringValue ("ctrl")); - tapBridge.Install (controllerNode, ctrlDev); + tapBridge.SetAttribute("Mode", StringValue("ConfigureLocal")); + tapBridge.SetAttribute("DeviceName", StringValue("ctrl")); + tapBridge.Install(controllerNode, ctrlDev); // ... diff --git a/examples/ofswitch13-custom-switch.cc b/examples/ofswitch13-custom-switch.cc index 3b59fb1..e4bdada 100644 --- a/examples/ofswitch13-custom-switch.cc +++ b/examples/ofswitch13-custom-switch.cc @@ -124,10 +124,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Configure ping application between hosts PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); diff --git a/examples/ofswitch13-external-controller.cc b/examples/ofswitch13-external-controller.cc index 32bb655..2bb6c66 100644 --- a/examples/ofswitch13-external-controller.cc +++ b/examples/ofswitch13-external-controller.cc @@ -153,10 +153,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Random number generators for ping applications Ptr randomHostRng = CreateObject(); diff --git a/examples/ofswitch13-first.cc b/examples/ofswitch13-first.cc index 6eac7f4..b0a2297 100644 --- a/examples/ofswitch13-first.cc +++ b/examples/ofswitch13-first.cc @@ -105,10 +105,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Configure ping application between hosts PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); diff --git a/examples/ofswitch13-logical-port/gtp-tunnel-app.cc b/examples/ofswitch13-logical-port/gtp-tunnel-app.cc index 75ad947..124808f 100644 --- a/examples/ofswitch13-logical-port/gtp-tunnel-app.cc +++ b/examples/ofswitch13-logical-port/gtp-tunnel-app.cc @@ -46,9 +46,12 @@ GtpTunnelApp::~GtpTunnelApp() TypeId GtpTunnelApp::GetTypeId() { - static TypeId tid = - TypeId("ns3::GtpTunnelApp").SetParent().SetGroupName("OFSwitch13"); + // clang-format off + static TypeId tid = TypeId("ns3::GtpTunnelApp") + .SetParent() + .SetGroupName("OFSwitch13"); return tid; + // clang-format on } bool @@ -116,9 +119,9 @@ GtpTunnelApp::RecvFromTunnelSocket(Ptr socket) // and the source address we set here using the physical device. AddHeader(packet, Mac48Address::ConvertFrom(m_physicalDev->GetAddress())); - // Send the packet to the OpenFlow switch over the logical port. - // Don't worry about source and destination addresses becasu they are note - // used by the receive method. + // Send the packet to the OpenFlow switch over the logical port. Don't worry + // about source and destination addresses because they are note used by the + // receive method. m_logicalPort->Receive(packet, Ipv4L3Protocol::PROT_NUMBER, Mac48Address(), @@ -163,8 +166,9 @@ GtpTunnelApp::AddHeader(Ptr packet, NS_LOG_FUNCTION(this << packet << source << dest << protocolNo); // All Ethernet frames must carry a minimum payload of 46 bytes. We need to - // pad out if we don't have enough bytes. These must be real bytes since they - // will be written to pcap files and compared in regression trace files. + // pad out if we don't have enough bytes. These must be real bytes since + // they will be written to pcap files and compared in regression trace + // files. if (packet->GetSize() < 46) { uint8_t buffer[46]; diff --git a/examples/ofswitch13-logical-port/gtp-tunnel-app.h b/examples/ofswitch13-logical-port/gtp-tunnel-app.h index 5206970..03fc10b 100644 --- a/examples/ofswitch13-logical-port/gtp-tunnel-app.h +++ b/examples/ofswitch13-logical-port/gtp-tunnel-app.h @@ -65,8 +65,8 @@ class GtpTunnelApp : public Application * Method to be assigned to the send callback of the VirtualNetDevice * implementing the OpenFlow logical port. It is called when the OpenFlow * switch sends a packet out over the logical port. The logical port - * callbacks here, and we must encapsulate the packet withing GTP and forward - * it to the UDP tunnel socket. + * callbacks here, and we must encapsulate the packet withing GTP and + * forward it to the UDP tunnel socket. * \param packet The packet received from the logical port. * \param source Ethernet source address. * \param dest Ethernet destination address. @@ -79,9 +79,9 @@ class GtpTunnelApp : public Application uint16_t protocolNo); /** - * Method to be assigned to the receive callback of the UDP tunnel socket. It - * is called when the tunnel socket receives a packet, and must forward the - * packet to the logical port. + * Method to be assigned to the receive callback of the UDP tunnel socket. + * It is called when the tunnel socket receives a packet, and must forward + * the packet to the logical port. * \param socket Pointer to the tunnel socket. */ void RecvFromTunnelSocket(Ptr socket); diff --git a/examples/ofswitch13-logical-port/main.cc b/examples/ofswitch13-logical-port/main.cc index 0df2837..95c1684 100644 --- a/examples/ofswitch13-logical-port/main.cc +++ b/examples/ofswitch13-logical-port/main.cc @@ -163,9 +163,9 @@ main(int argc, char* argv[]) hostStaticRouting->AddNetworkRouteTo(ipDomain1, mask24, sw1GatewayIp, 1); // Connect switch 0 to switch 1. These CSMA devices will not be added as - // switch ports. Instead, each one will be configured as standard ns-3 device - // (with IP address and an UDP socket binded to it). They will be used to - // implement the UDP/IP tunneling process. The GtpTunnelApp application + // switch ports. Instead, each one will be configured as standard ns-3 + // device (with IP address and an UDP socket bound to it). They will be used + // to implement the UDP/IP tunneling process. The GtpTunnelApp application // running on top of the UDP socket will be in charge of adding and removing // the GTP headers, and forwarding the packets to a VirtualNetDevice device // on the same node. This VirtualNetDevice will be configured as switch port @@ -214,7 +214,7 @@ main(int argc, char* argv[]) ApplicationContainer pingApps = pingHelper.Install(hosts.Get(0)); pingApps.Start(Seconds(1)); - // Enable datapath stats and pcap traces at hosts, switch(es), and controller(s) + // Enable datapath stats and pcap traces at hosts, switches, and controllers if (trace) { of13Helper->EnableOpenFlowPcap("openflow"); diff --git a/examples/ofswitch13-logical-port/tunnel-controller.cc b/examples/ofswitch13-logical-port/tunnel-controller.cc index 1a778ff..5bbac8c 100644 --- a/examples/ofswitch13-logical-port/tunnel-controller.cc +++ b/examples/ofswitch13-logical-port/tunnel-controller.cc @@ -99,9 +99,7 @@ TunnelController::HandshakeSuccessful(Ptr swtch) uint64_t swDpId = swtch->GetDpId(); // Send ARP requests to controller. - DpctlExecute(swDpId, - "flow-mod cmd=add,table=0,prio=16 eth_type=0x0806 " - "apply:output=ctrl"); + DpctlExecute(swDpId, "flow-mod cmd=add,table=0,prio=16 eth_type=0x0806 apply:output=ctrl"); // Table miss entry. DpctlExecute(swDpId, "flow-mod cmd=add,table=0,prio=0 apply:output=ctrl"); @@ -132,10 +130,10 @@ TunnelController::HandlePacketIn(struct ofl_msg_packet_in* msg, if (inPort == 1) { - // IP packets entering the switch from the physical port 1 are coming - // from the host node. In this case, identify and set TEID and tunnel - // endpoint IPv4 address into tunnel metadata, and output the packet - // on the logical port 2. + // IP packets entering the switch from the physical port 1 are + // coming from the host node. In this case, identify and set TEID + // and tunnel endpoint IPv4 address into tunnel metadata, and output + // the packet on the logical port 2. Ipv4Address dstAddr = GetTunnelEndpoint(swDpId, 2); uint64_t tunnelId = static_cast(dstAddr.Get()) << 32; tunnelId |= 0xFFFF; @@ -155,13 +153,14 @@ TunnelController::HandlePacketIn(struct ofl_msg_packet_in* msg, else if (inPort == 2) { // IP packets entering the switch from the logical port have already - // been de-encapsulated by the logical port operation, and the tunnel - // id must match the arbitrary value 0xFFFF defined set above. Theses - // packets must be forwarded to the host on the physical port 1. In - // this case, the OpenFlow switch is acting as a router, and we need - // to set the host destination MAC addresses. Note that the packet - // leaving the OpenFlow pipeline will not be sent to the IP layer, so - // no ARP resolution is available and we need to do it manually here. + // been de-encapsulated by the logical port operation, and the + // tunnel id must match the arbitrary value 0xFFFF defined set + // above. Theses packets must be forwarded to the host on the + // physical port 1. In this case, the OpenFlow switch is acting as a + // router, and we need to set the host destination MAC addresses. + // Note that the packet leaving the OpenFlow pipeline will not be + // sent to the IP layer, so no ARP resolution is available and we + // need to do it manually here. Ipv4Address dstIp = ExtractIpv4Address(OXM_OF_IPV4_DST, (struct ofl_match*)msg->match); Mac48Address dstMac = GetArpEntry(dstIp); diff --git a/examples/ofswitch13-multiple-controllers.cc b/examples/ofswitch13-multiple-controllers.cc index 7683e13..aeff24b 100644 --- a/examples/ofswitch13-multiple-controllers.cc +++ b/examples/ofswitch13-multiple-controllers.cc @@ -113,10 +113,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Configure ping application between hosts PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); diff --git a/examples/ofswitch13-multiple-domains.cc b/examples/ofswitch13-multiple-domains.cc index b3f4217..6a09bed 100644 --- a/examples/ofswitch13-multiple-domains.cc +++ b/examples/ofswitch13-multiple-domains.cc @@ -127,10 +127,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Configure ping application between hosts PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); diff --git a/examples/ofswitch13-qos-controller/main.cc b/examples/ofswitch13-qos-controller/main.cc index 4b3292f..5218e1c 100644 --- a/examples/ofswitch13-qos-controller/main.cc +++ b/examples/ofswitch13-qos-controller/main.cc @@ -178,7 +178,8 @@ main(int argc, char* argv[]) Ptr qosCtrl = CreateObject(); ofQosHelper->InstallController(controllerNodes.Get(0), qosCtrl); - // Configure OpenFlow learning controller for client switch (#2) into controller node 1 + // Configure OpenFlow learning controller for client switch (#2) into + // controller node 1 Ptr ofLearningHelper = CreateObject(); Ptr learnCtrl = CreateObject(); ofLearningHelper->InstallController(controllerNodes.Get(1), learnCtrl); @@ -198,7 +199,7 @@ main(int argc, char* argv[]) internet.Install(serverNodes); internet.Install(clientNodes); - // Set IPv4 server and client addresses (discarding the first server address) + // Set IPv4 server and client addresses (discarding first server address) Ipv4AddressHelper ipv4switches; Ipv4InterfaceContainer internetIpIfaces; ipv4switches.SetBase("10.1.0.0", "255.255.0.0", "0.0.1.2"); diff --git a/examples/ofswitch13-qos-controller/qos-controller.cc b/examples/ofswitch13-qos-controller/qos-controller.cc index 0eea1da..44d910e 100644 --- a/examples/ofswitch13-qos-controller/qos-controller.cc +++ b/examples/ofswitch13-qos-controller/qos-controller.cc @@ -226,7 +226,7 @@ QosController::ConfigureAggregationSwitch(Ptr swtch) "weight=0,port=any,group=any output=1"); } - // Packets from input ports 1 and 2 are redirecte to port 3 + // Packets from input ports 1 and 2 are redirected to port 3 DpctlExecute(swDpId, "flow-mod cmd=add,table=0,prio=500 " "in_port=1 write:output=3"); diff --git a/examples/ofswitch13-qos-controller/qos-controller.h b/examples/ofswitch13-qos-controller/qos-controller.h index d3ceeb8..c04acd9 100644 --- a/examples/ofswitch13-qos-controller/qos-controller.h +++ b/examples/ofswitch13-qos-controller/qos-controller.h @@ -140,7 +140,7 @@ class QosController : public OFSwitch13Controller Address m_serverIpAddress; //!< Virtual server IP address uint16_t m_serverTcpPort; //!< Virtual server TCP port Address m_serverMacAddress; //!< Border switch MAC address - bool m_meterEnable; //!< Enable per-flow mettering + bool m_meterEnable; //!< Enable per-flow metering DataRate m_meterRate; //!< Per-flow meter rate bool m_linkAggregation; //!< Enable link aggregation diff --git a/examples/ofswitch13-single-domain.cc b/examples/ofswitch13-single-domain.cc index 6c99131..63d70ce 100644 --- a/examples/ofswitch13-single-domain.cc +++ b/examples/ofswitch13-single-domain.cc @@ -124,10 +124,10 @@ main(int argc, char* argv[]) internet.Install(hosts); // Set IPv4 host addresses - Ipv4AddressHelper ipv4helpr; + Ipv4AddressHelper ipv4Helper; Ipv4InterfaceContainer hostIpIfaces; - ipv4helpr.SetBase("10.1.1.0", "255.255.255.0"); - hostIpIfaces = ipv4helpr.Assign(hostDevices); + ipv4Helper.SetBase("10.1.1.0", "255.255.255.0"); + hostIpIfaces = ipv4Helper.Assign(hostDevices); // Configure ping application between hosts PingHelper pingHelper(Ipv4Address(hostIpIfaces.GetAddress(1))); diff --git a/helper/ofswitch13-device-container.cc b/helper/ofswitch13-device-container.cc index 6b89190..3eb768e 100644 --- a/helper/ofswitch13-device-container.cc +++ b/helper/ofswitch13-device-container.cc @@ -39,9 +39,8 @@ OFSwitch13DeviceContainer::OFSwitch13DeviceContainer(std::string devName) m_devices.emplace_back(dev); } -OFSwitch13DeviceContainer::OFSwitch13DeviceContainer( - const OFSwitch13DeviceContainer& a, - const OFSwitch13DeviceContainer& b) +OFSwitch13DeviceContainer::OFSwitch13DeviceContainer(const OFSwitch13DeviceContainer& a, + const OFSwitch13DeviceContainer& b) { *this = a; Add(b); diff --git a/helper/ofswitch13-device-container.h b/helper/ofswitch13-device-container.h index 9e97327..f049ad3 100644 --- a/helper/ofswitch13-device-container.h +++ b/helper/ofswitch13-device-container.h @@ -35,11 +35,10 @@ namespace ns3 * Typically OpenFlow Devices are aggregated to nodes using the * OFSwitch13Helper. The helper InstallSwitch* methods takes a NodeContainer * which holds some number of Ptr. For each of the Nodes in the - * NodeContainer the helper will instantiate an OpenFlow device and aggregate - * it to the node. For each of the devices, the helper also adds the device - * into a Container for later use by the caller. This is that container used to - * hold the Ptr which are instantiated by the device - * helper. + * NodeContainer the helper will instantiate an OpenFlow device and aggregate it + * to the node. For each of the devices, the helper also adds the device into a + * Container for later use by the caller. This is that container used to hold + * the Ptr which are instantiated by the device helper. */ class OFSwitch13DeviceContainer { @@ -55,7 +54,6 @@ class OFSwitch13DeviceContainer /** * Create a OFSwitch13DeviceContainer with exactly one device that has * previously been instantiated. - * * \param dev An OpenFlow device to add to the container. */ OFSwitch13DeviceContainer(Ptr dev); @@ -65,7 +63,6 @@ class OFSwitch13DeviceContainer * OFSwitch13DeviceContainer with exactly one device which has been * previously instantiated and assigned a name using the Object name * service. This OpenFlow device is specified by its assigned name. - * * \param devName The name of the device to add to the container. */ OFSwitch13DeviceContainer(std::string devName); @@ -73,7 +70,6 @@ class OFSwitch13DeviceContainer /** * Create a device container which is a concatenation of the two input * OFSwitch13DeviceContainers. - * * \note A frequently seen idiom that uses these constructors involves the * implicit conversion by constructor of Ptr. When used, * two Ptr will be passed to this constructor instead of @@ -81,7 +77,6 @@ class OFSwitch13DeviceContainer * that goes through the OFSwitch13DeviceContainer (Ptr * dev) constructor above. Using this conversion one may provide optionally * provide arguments of Ptr to these constructors. - * * \param a A device container * \param b Another device container * @@ -92,63 +87,54 @@ class OFSwitch13DeviceContainer /** * \brief Get an iterator which refers to the first OpenFlow device in the * container. - * * OpenFlow devices can be retrieved from the container in two ways. First, * directly by an index into the container, and second, using an iterator. * This method is used in the iterator method and is typically used in a * for-loop to run through the devices. - * * \code * OFSwitch13DeviceContainer::Iterator i; - * for (i = container.Begin (); i != container.End (); ++i) - * { - * (*i)->method (); // some OFSwitch13Device method - * } + * for (i = container.Begin(); i != container.End(); ++i) + * { + * (*i)->method(); // some OFSwitch13Device method + * } * \endcode - * * \returns an iterator which refers to the first device in the container. */ Iterator Begin() const; /** - * \brief Get an iterator which indicates past-the-last OpenFlow device in + * \brief Get an iterator which indicates past-the-last OpenFlow device in * the container. - * * OpenFlow devices can be retrieved from the container in two ways. First, * directly by an index into the container, and second, using an iterator. * This method is used in the iterator method and is typically used in a * for-loop to run through the devices - * * \code * OFSwitch13DeviceContainer::Iterator i; - * for (i = container.Begin (); i != container.End (); ++i) - * { - * (*i)->method (); // some OFSwitch13Device method - * } + * for (i = container.Begin(); i != container.End(); ++i) + * { + * (*i)->method(); // some OFSwitch13Device method + * } * \endcode - * * \returns an iterator which indicates an ending condition for a loop. */ Iterator End() const; /** * \brief Get the number of Ptr stored in this container. - * * OpenFlow devices can be retrieved from the container in two ways. First, * directly by an index into the container, and second, using an iterator. * This method is used in the direct method and is typically used to * define an ending condition in a for-loop that runs through the stored * devices - * * \code - * uint32_t nDevices = container.GetN (); + * uint32_t nDevices = container.GetN(); * for (uint32_t i = 0 i < nDevices; ++i) - * { - * Ptr p = container.Get (i); - * p->method (); // some OFSwitch13Device method - * } + * { + * Ptr p = container.Get(i); + * p->method(); // some OFSwitch13Device method + * } * \endcode - * * \returns the number of Ptr stored in this container. */ uint32_t GetN() const; @@ -156,21 +142,18 @@ class OFSwitch13DeviceContainer /** * \brief Get the Ptr stored in this container at a given * index. - * * OpenFlow devices can be retrieved from the container in two ways.First, * directly by an index into the container, and second, using an iterator. * This method is used in the direct method and is used to retrieve the * indexed Ptr. - * * \code - * uint32_t nDevices = container.GetN (); + * uint32_t nDevices = container.GetN(); * for (uint32_t i = 0 i < nDevices; ++i) - * { - * Ptr p = container.Get (i); - * p->method (); // some OFSwitch13Device method - * } + * { + * Ptr p = container.Get(i); + * p->method(); // some OFSwitch13Device method + * } * \endcode - * * \param i the index of the requested device pointer. * \returns the requested device pointer. */ @@ -179,14 +162,12 @@ class OFSwitch13DeviceContainer /** * \brief Append the contents of another OFSwitch13DeviceContainer to the * end of this container. - * * \param other The OFSwitch13DeviceContainer to append. */ void Add(OFSwitch13DeviceContainer other); /** * \brief Append a single Ptr to this container. - * * \param device The Ptr to append. */ void Add(Ptr device); @@ -194,7 +175,6 @@ class OFSwitch13DeviceContainer /** * \brief Append to this container the single Ptr referred * to via its object name service registered name. - * * \param deviceName The name of the OFSwitch13Device object to add to the * container. */ diff --git a/helper/ofswitch13-external-helper.cc b/helper/ofswitch13-external-helper.cc index de31ab2..ce7e1db 100644 --- a/helper/ofswitch13-external-helper.cc +++ b/helper/ofswitch13-external-helper.cc @@ -50,12 +50,11 @@ OFSwitch13ExternalHelper::GetTypeId() .SetParent() .SetGroupName("OFSwitch13") .AddConstructor() - .AddAttribute( - "Port", - "The port number where controller will be available.", - UintegerValue(6653), - MakeUintegerAccessor(&OFSwitch13ExternalHelper::m_controlPort), - MakeUintegerChecker()); + .AddAttribute("Port", + "The port number where controller will be available.", + UintegerValue(6653), + MakeUintegerAccessor(&OFSwitch13ExternalHelper::m_controlPort), + MakeUintegerChecker()); return tid; } @@ -66,8 +65,7 @@ OFSwitch13ExternalHelper::SetChannelType(ChannelType type) // Check for valid channel type for this helper. NS_ABORT_MSG_IF(type != OFSwitch13Helper::SINGLECSMA, - "Invalid channel " - "type for OFSwitch13ExternalHelper (use SingleCsma)."); + "Invalid channel type for OFSwitch13ExternalHelper (use SingleCsma)."); OFSwitch13Helper::SetChannelType(type); } @@ -95,8 +93,7 @@ OFSwitch13ExternalHelper::CreateOpenFlowChannels() switch (m_channelType) { case OFSwitch13ExternalHelper::SINGLECSMA: { - NS_LOG_INFO("Attach all switches and controllers to the same " - "CSMA network."); + NS_LOG_INFO("Attach all switches and controllers to the same CSMA network."); // Connecting all switches to the common channel. NetDeviceContainer switchDevices; @@ -106,15 +103,11 @@ OFSwitch13ExternalHelper::CreateOpenFlowChannels() // Start the connections between controller and switches. OFSwitch13DeviceContainer::Iterator ofDev; - for (ofDev = m_openFlowDevs.Begin(); ofDev != m_openFlowDevs.End(); - ofDev++) + for (ofDev = m_openFlowDevs.Begin(); ofDev != m_openFlowDevs.End(); ofDev++) { - NS_LOG_INFO("Connect switch " << (*ofDev)->GetDatapathId() - << " to controller " << addr.GetIpv4() - << " port " << addr.GetPort()); - Simulator::ScheduleNow(&OFSwitch13Device::StartControllerConnection, - *ofDev, - addr); + NS_LOG_INFO("Connect switch " << (*ofDev)->GetDatapathId() << " to controller " + << addr.GetIpv4() << " port " << addr.GetPort()); + Simulator::ScheduleNow(&OFSwitch13Device::StartControllerConnection, *ofDev, addr); } m_ipv4helper.NewNetwork(); break; @@ -133,8 +126,7 @@ OFSwitch13ExternalHelper::InstallExternalController(Ptr cNode) NS_LOG_FUNCTION(this << cNode); NS_LOG_INFO("Installing OpenFlow controller on node " << cNode->GetId()); - NS_ABORT_MSG_IF(m_blocked || m_controlDevs.GetN() != 0, - "OpenFlow controller/channels already configured."); + NS_ABORT_MSG_IF(m_blocked || m_controlDevs.GetN() != 0, "OpenFlow channel already configured."); // Install the TCP/IP stack into controller node. if (!cNode->GetObject()) diff --git a/helper/ofswitch13-external-helper.h b/helper/ofswitch13-external-helper.h index 64e49af..1260852 100644 --- a/helper/ofswitch13-external-helper.h +++ b/helper/ofswitch13-external-helper.h @@ -62,7 +62,6 @@ class OFSwitch13ExternalHelper : public OFSwitch13Helper * the local machine over a TapBridge device. It installs the TCP/IP stack * into controller node, attach it to the common CSMA channel and configure * IP address for it. - * * \param cNode The node to configure as the controller. * \return The network device to bind to the TapBridge. */ diff --git a/helper/ofswitch13-helper.cc b/helper/ofswitch13-helper.cc index d523ea7..ecc0e14 100644 --- a/helper/ofswitch13-helper.cc +++ b/helper/ofswitch13-helper.cc @@ -31,8 +31,7 @@ namespace ns3 NS_LOG_COMPONENT_DEFINE("OFSwitch13Helper"); NS_OBJECT_ENSURE_REGISTERED(OFSwitch13Helper); -Ipv4AddressHelper OFSwitch13Helper::m_ipv4helper = - Ipv4AddressHelper("10.100.0.0", "255.255.255.0"); +Ipv4AddressHelper OFSwitch13Helper::m_ipv4helper = Ipv4AddressHelper("10.100.0.0", "255.255.255.0"); class OFSwitch13Controller; @@ -57,24 +56,22 @@ OFSwitch13Helper::GetTypeId() TypeId("ns3::OFSwitch13Helper") .SetParent() .SetGroupName("OFSwitch13") - .AddAttribute( - "ChannelDataRate", - "The data rate to be used for the OpenFlow channel.", - DataRateValue(DataRate("10Gb/s")), - MakeDataRateAccessor(&OFSwitch13Helper::SetChannelDataRate), - MakeDataRateChecker()) - .AddAttribute( - "ChannelType", - "The configuration used to create the OpenFlow channel", - TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, - EnumValue(OFSwitch13Helper::SINGLECSMA), - MakeEnumAccessor(&OFSwitch13Helper::SetChannelType), - MakeEnumChecker(OFSwitch13Helper::SINGLECSMA, - "SingleCsma", - OFSwitch13Helper::DEDICATEDCSMA, - "DedicatedCsma", - OFSwitch13Helper::DEDICATEDP2P, - "DedicatedP2p")); + .AddAttribute("ChannelDataRate", + "The data rate to be used for the OpenFlow channel.", + DataRateValue(DataRate("10Gb/s")), + MakeDataRateAccessor(&OFSwitch13Helper::SetChannelDataRate), + MakeDataRateChecker()) + .AddAttribute("ChannelType", + "The configuration used to create the OpenFlow channel", + TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, + EnumValue(OFSwitch13Helper::SINGLECSMA), + MakeEnumAccessor(&OFSwitch13Helper::SetChannelType), + MakeEnumChecker(OFSwitch13Helper::SINGLECSMA, + "SingleCsma", + OFSwitch13Helper::DEDICATEDCSMA, + "DedicatedCsma", + OFSwitch13Helper::DEDICATEDP2P, + "DedicatedP2p")); return tid; } @@ -91,7 +88,7 @@ OFSwitch13Helper::SetChannelType(ChannelType type) { NS_LOG_FUNCTION(this << type); - // Set the channel type and address, which will select proper netowrk mask. + // Set the channel type and address, which will select proper network mask. m_channelType = type; } @@ -167,7 +164,7 @@ OFSwitch13Helper::EnableDatapathStats(std::string prefix, bool useNodeNames) const std::string extension = ".log"; // Iterate over the container and for each OpenFlow devices create a stats - // calculator to monitor datapath statistcs. + // calculator to monitor datapath statistics. OFSwitch13DeviceContainer::Iterator it; for (it = m_openFlowDevs.Begin(); it != m_openFlowDevs.End(); it++) { @@ -256,9 +253,7 @@ OFSwitch13Helper::InstallSwitch(NodeContainer& swNodes) } void -OFSwitch13Helper::SetAddressBase(Ipv4Address network, - Ipv4Mask mask, - Ipv4Address base) +OFSwitch13Helper::SetAddressBase(Ipv4Address network, Ipv4Mask mask, Ipv4Address base) { NS_LOG_FUNCTION_NOARGS(); diff --git a/helper/ofswitch13-helper.h b/helper/ofswitch13-helper.h index ede92c9..48480eb 100644 --- a/helper/ofswitch13-helper.h +++ b/helper/ofswitch13-helper.h @@ -55,19 +55,19 @@ class OFSwitch13LearningController; * * By default, the connections between switches and controllers are created * using a single shared out-of-band CSMA channel, with IP addresses assigned - * using a /24 network mask. Users can modify this configuration by changing - * the ChannelType attribute at instantiation time. Dedicated out-of-band + * using a /24 network mask. Users can modify this configuration by changing the + * ChannelType attribute at instantiation time. Dedicated out-of-band * connections over CSMA or Point-to-Point channels are also available, using a * /30 network mask for IP allocation. * * Please note that this base helper class was designed to configure a single - * OpenFlow network domain. All switches will be connected to all controllers - * on the same domain. If you want to configure separated OpenFlow domains on - * your network topology (with their individual switches and controllers) so - * you may need to use a different instance of the derived helper class for - * each domain. In this case, don't forget to use the SetAddressBase () - * method to change the IP network address of the other helper instances, in - * order to avoid IP conflicts. + * OpenFlow network domain. All switches will be connected to all controllers on + * the same domain. If you want to configure separated OpenFlow domains on your + * network topology (with their individual switches and controllers) so you may + * need to use a different instance of the derived helper class for each domain. + * In this case, don't forget to use the SetAddressBase() method to change the + * IP network address of the other helper instances, in order to avoid IP + * conflicts. */ class OFSwitch13Helper : public Object { @@ -94,7 +94,6 @@ class OFSwitch13Helper : public Object /** * Set an attribute on each OpenFlow device created by this helper. - * * \param n1 the name of the attribute to set. * \param v1 the value of the attribute to set. */ @@ -103,7 +102,6 @@ class OFSwitch13Helper : public Object /** * Set the OpenFlow channel type used to create the connections between * switches and controllers. - * * \param type The ChannelType to use. */ virtual void SetChannelType(ChannelType type); @@ -111,27 +109,21 @@ class OFSwitch13Helper : public Object /** * Set the OpenFlow channel data rate used to create the connections between * switches and controllers. - * * \param rate The channel data rate to use. */ virtual void SetChannelDataRate(DataRate rate); /** - * Enable pacp traces at OpenFlow channel between controller and switches. - * + * Enable pcap traces at OpenFlow channel between controller and switches. * \attention Call this method only after configuring the OpenFlow channels. - * * \param prefix Filename prefix to use for pcap files. * \param promiscuous If true, enable promisc trace. */ - void EnableOpenFlowPcap(std::string prefix = "ofchannel", - bool promiscuous = true); + void EnableOpenFlowPcap(std::string prefix = "ofchannel", bool promiscuous = true); /** * Enable ASCII traces at OpenFlow channel between controller and switches. - * * \attention Call this method only after configuring the OpenFlow channels. - * * \param prefix Filename prefix to use for ascii files. */ void EnableOpenFlowAscii(std::string prefix = "ofchannel"); @@ -139,34 +131,28 @@ class OFSwitch13Helper : public Object /** * Enable OpenFlow datapath statistics at OpenFlow switch devices configured * by this helper. This method will create an OFSwitch13StatsCalculator for - * each switch device, dumping statistcs to output files. - * + * each switch device, dumping statistics to output files. * \attention Call this method only after configuring the OpenFlow channels. - * * \param prefix Filename prefix to use for stats files. * \param useNodeNames Use node names instead of datapath id. */ - void EnableDatapathStats(std::string prefix = "datapath", - bool useNodeNames = false); + void EnableDatapathStats(std::string prefix = "datapath", bool useNodeNames = false); /** * This method creates an OpenFlow device and aggregates it to the switch * node. It also attaches the given devices as physical ports on the switch. * If no devices are given, the switch will be configured without ports. In * this case, don't forget to add ports to it later, or it will do nothing. - * * \param swNode The switch node where to install the OpenFlow device. * \param ports Container of devices to be added as physical switch ports. * \return The OpenFlow device created. */ - Ptr InstallSwitch(Ptr swNode, - NetDeviceContainer& swPorts); + Ptr InstallSwitch(Ptr swNode, NetDeviceContainer& swPorts); /** * This method creates an OpenFlow device and aggregates it to the switch * node. The switch configured by this method will have no switch ports. * Don't forget to add ports do it later, or it will do nothing. - * * \param swNode The switch node where to install the OpenFlow device. * \return The OpenFlow device created. */ @@ -176,7 +162,6 @@ class OFSwitch13Helper : public Object * This method creates and aggregates an OpenFlow device to each switch node * in the container. Switches configured by this method will have no switch * ports. Don't forget to add ports do them later, or they will do nothing. - * * \param swNodes The switch nodes where to install the OpenFlow devices. * \return A container holding all the OpenFlow devices created. */ @@ -193,8 +178,7 @@ class OFSwitch13Helper : public Object /** * Set the IP network base address, used to assign IP addresses to switches - * and controllers during the CreateOpenFlowChannels () procedure. - * + * and controllers during the CreateOpenFlowChannels() procedure. * \param network The Ipv4Address containing the initial network number to * use during allocation. * \param mask The Ipv4Mask containing one bits in each bit position of the @@ -202,20 +186,16 @@ class OFSwitch13Helper : public Object * \param base An optional Ipv4Address containing the initial address used * for IP address allocation. */ - static void SetAddressBase(Ipv4Address network, - Ipv4Mask mask, - Ipv4Address base = "0.0.0.1"); + static void SetAddressBase(Ipv4Address network, Ipv4Mask mask, Ipv4Address base = "0.0.0.1"); /** * Enable OpenFlow datapath logs at all OpenFlow switch devices on the * simulation. This method will enable vlog system at debug level on the * BOFUSS library, dumping messages to output file. - * * \param prefix Filename prefix to use for log file. * \param explicitFilename Treat the prefix as an explicit filename if true. */ - static void EnableDatapathLogs(std::string prefix = "", - bool explicitFilename = false); + static void EnableDatapathLogs(std::string prefix = "", bool explicitFilename = false); protected: /** Destructor implementation. */ diff --git a/helper/ofswitch13-internal-helper.cc b/helper/ofswitch13-internal-helper.cc index 6782211..6c08c1a 100644 --- a/helper/ofswitch13-internal-helper.cc +++ b/helper/ofswitch13-internal-helper.cc @@ -66,13 +66,11 @@ OFSwitch13InternalHelper::CreateOpenFlowChannels() switch (m_channelType) { case OFSwitch13InternalHelper::SINGLECSMA: { - NS_LOG_INFO("Attach all switches and controllers to the same " - "CSMA network."); + NS_LOG_INFO("Attach all switches and controllers to the same CSMA network."); // Create the common channel for all switches and controllers. - Ptr csmaChannel = CreateObjectWithAttributes( - "DataRate", - DataRateValue(m_channelDataRate)); + Ptr csmaChannel = + CreateObjectWithAttributes("DataRate", DataRateValue(m_channelDataRate)); // Connecting all switches and controllers to the common channel. NetDeviceContainer switchDevices; @@ -87,20 +85,14 @@ OFSwitch13InternalHelper::CreateOpenFlowChannels() for (uint32_t ctIdx = 0; ctIdx < controllerAddrs.GetN(); ctIdx++) { m_controlApps.Get(ctIdx)->GetAttribute("Port", portValue); - InetSocketAddress addr(controllerAddrs.GetAddress(ctIdx), - portValue.Get()); + InetSocketAddress addr(controllerAddrs.GetAddress(ctIdx), portValue.Get()); OFSwitch13DeviceContainer::Iterator ofDev; - for (ofDev = m_openFlowDevs.Begin(); ofDev != m_openFlowDevs.End(); - ofDev++) + for (ofDev = m_openFlowDevs.Begin(); ofDev != m_openFlowDevs.End(); ofDev++) { - NS_LOG_INFO("Connect switch " - << (*ofDev)->GetDatapathId() << " to controller " - << addr.GetIpv4() << " port " << addr.GetPort()); - Simulator::ScheduleNow( - &OFSwitch13Device::StartControllerConnection, - *ofDev, - addr); + NS_LOG_INFO("Connect switch " << (*ofDev)->GetDatapathId() << " to controller " + << addr.GetIpv4() << " port " << addr.GetPort()); + Simulator::ScheduleNow(&OFSwitch13Device::StartControllerConnection, *ofDev, addr); } } m_ipv4helper.NewNetwork(); @@ -109,25 +101,19 @@ OFSwitch13InternalHelper::CreateOpenFlowChannels() case OFSwitch13InternalHelper::DEDICATEDCSMA: case OFSwitch13InternalHelper::DEDICATEDP2P: { // Setting channel/device data rates. - m_p2pHelper.SetDeviceAttribute("DataRate", - DataRateValue(m_channelDataRate)); - m_csmaHelper.SetChannelAttribute("DataRate", - DataRateValue(m_channelDataRate)); + m_p2pHelper.SetDeviceAttribute("DataRate", DataRateValue(m_channelDataRate)); + m_csmaHelper.SetChannelAttribute("DataRate", DataRateValue(m_channelDataRate)); // To avoid IP datagram fragmentation, we are configuring the OpenFlow // channel devices with a very large MTU value. The TCP sockets used to - // send packets to theses devices are also configured to use a large + // send packets to these devices are also configured to use a large // segment size at OFSwitch13Controller and OFSwitch13Device. m_csmaHelper.SetDeviceAttribute("Mtu", UintegerValue(9000)); m_p2pHelper.SetDeviceAttribute("Mtu", UintegerValue(9000)); // Using large queues on devices to avoid losing packets. - m_csmaHelper.SetQueue("ns3::DropTailQueue", - "MaxSize", - StringValue("65536p")); - m_p2pHelper.SetQueue("ns3::DropTailQueue", - "MaxSize", - StringValue("65536p")); + m_csmaHelper.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("65536p")); + m_p2pHelper.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("65536p")); // Create individual channels for each pair switch/controller. UintegerValue portValue; @@ -143,21 +129,15 @@ OFSwitch13InternalHelper::CreateOpenFlowChannels() NetDeviceContainer pairDevs = Connect(ctNode, swNode); m_controlDevs.Add(pairDevs.Get(0)); - Ipv4InterfaceContainer pairIfaces = - m_ipv4helper.Assign(pairDevs); + Ipv4InterfaceContainer pairIfaces = m_ipv4helper.Assign(pairDevs); // Start this single connection between switch and controller. m_controlApps.Get(ctIdx)->GetAttribute("Port", portValue); - InetSocketAddress addr(pairIfaces.GetAddress(0), - portValue.Get()); - - NS_LOG_INFO("Connect switch " - << ofDev->GetDatapathId() << " to controller " - << addr.GetIpv4() << " port " << addr.GetPort()); - Simulator::ScheduleNow( - &OFSwitch13Device::StartControllerConnection, - ofDev, - addr); + InetSocketAddress addr(pairIfaces.GetAddress(0), portValue.Get()); + + NS_LOG_INFO("Connect switch " << ofDev->GetDatapathId() << " to controller " + << addr.GetIpv4() << " port " << addr.GetPort()); + Simulator::ScheduleNow(&OFSwitch13Device::StartControllerConnection, ofDev, addr); m_ipv4helper.NewNetwork(); } } @@ -170,9 +150,7 @@ OFSwitch13InternalHelper::CreateOpenFlowChannels() } Ptr -OFSwitch13InternalHelper::InstallController( - Ptr cNode, - Ptr controller) +OFSwitch13InternalHelper::InstallController(Ptr cNode, Ptr controller) { NS_LOG_FUNCTION(this << cNode << controller); diff --git a/helper/ofswitch13-internal-helper.h b/helper/ofswitch13-internal-helper.h index 36895cd..7f38d95 100644 --- a/helper/ofswitch13-internal-helper.h +++ b/helper/ofswitch13-internal-helper.h @@ -58,15 +58,13 @@ class OFSwitch13InternalHelper : public OFSwitch13Helper * This method installs the given controller application into the given * controller node. If no application is given, a new (default) learning * controller application is created and installed into controller node. - * * \param cNode The node to configure as controller. * \param controller The controller application to install into cNode * \return The installed controller application. */ Ptr InstallController( Ptr cNode, - Ptr controller = - CreateObject()); + Ptr controller = CreateObject()); protected: /** Destructor implementation. */ @@ -75,7 +73,6 @@ class OFSwitch13InternalHelper : public OFSwitch13Helper /** * Create an individual connection between the switch and the controller * node, using the already configured channel type. - * * \param ctrl The controller node. * \param swtch The switch node. * \return The devices created on both nodes. diff --git a/helper/ofswitch13-stats-calculator.cc b/helper/ofswitch13-stats-calculator.cc index f6e88b3..3443e35 100644 --- a/helper/ofswitch13-stats-calculator.cc +++ b/helper/ofswitch13-stats-calculator.cc @@ -72,37 +72,34 @@ OFSwitch13StatsCalculator::GetTypeId() .SetParent() .SetGroupName("OFSwitch13") .AddConstructor() - .AddAttribute( - "EwmaAlpha", - "The EWMA alpha parameter for averaging statistics.", - DoubleValue(0.2), - MakeDoubleAccessor(&OFSwitch13StatsCalculator::m_alpha), - MakeDoubleChecker(0.0, 1.0)) - .AddAttribute( - "DumpTimeout", - "The interval to update and dump switch statistics.", - TimeValue(Seconds(1)), - MakeTimeAccessor(&OFSwitch13StatsCalculator::m_timeout), - MakeTimeChecker(Seconds(1))) - .AddAttribute( - "OutputFilename", - "Filename for dumping switch statistics.", - TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, - StringValue("ofswitch_stats.log"), - MakeStringAccessor(&OFSwitch13StatsCalculator::m_filename), - MakeStringChecker()) - .AddAttribute( - "FlowTableDetails", - "Dump individual pipeline flow table statistics.", - BooleanValue(false), - MakeBooleanAccessor(&OFSwitch13StatsCalculator::m_details), - MakeBooleanChecker()); + .AddAttribute("EwmaAlpha", + "The EWMA alpha parameter for averaged statistics.", + DoubleValue(0.2), + MakeDoubleAccessor(&OFSwitch13StatsCalculator::m_alpha), + MakeDoubleChecker(0.0, 1.0)) + .AddAttribute("DumpTimeout", + "The interval to update and dump switch statistics.", + TimeValue(Seconds(1)), + MakeTimeAccessor(&OFSwitch13StatsCalculator::m_timeout), + MakeTimeChecker(Seconds(1))) + .AddAttribute("OutputFilename", + "Filename for dumping switch statistics.", + TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, + StringValue("ofswitch_stats.log"), + MakeStringAccessor(&OFSwitch13StatsCalculator::m_filename), + MakeStringChecker()) + .AddAttribute("FlowTableDetails", + "Dump individual pipeline flow table statistics.", + BooleanValue(false), + MakeBooleanAccessor(&OFSwitch13StatsCalculator::m_details), + MakeBooleanChecker()); return tid; } void OFSwitch13StatsCalculator::HookSinks(Ptr device) { + // clang-format off NS_LOG_FUNCTION(this << device); // Save switch device pointer. @@ -138,8 +135,8 @@ OFSwitch13StatsCalculator::HookSinks(Ptr device) { std::string field1 = "T" + to_string(i) + "Entr"; std::string field2 = "T" + to_string(i) + "Usag"; - *m_wrapper->GetStream() - << " " << setw(7) << field1 << " " << setw(7) << field2; + *m_wrapper->GetStream() << " " << setw(7) << field1 + << " " << setw(7) << field2; } } @@ -168,6 +165,7 @@ OFSwitch13StatsCalculator::HookSinks(Ptr device) Ptr(this))); m_ewmaFlowEntries.resize(device->GetNPipelineTables(), 0.0); + // clang-format on } uint32_t @@ -230,9 +228,8 @@ OFSwitch13StatsCalculator::GetAvgCpuUsage() const { return 0; } - return std::round( - static_cast(GetEwmaCpuLoad().GetBitRate()) * 100 / - static_cast(m_device->GetCpuCapacity().GetBitRate())); + return std::round(static_cast(GetEwmaCpuLoad().GetBitRate()) * 100 / + static_cast(m_device->GetCpuCapacity().GetBitRate())); } uint32_t @@ -242,8 +239,7 @@ OFSwitch13StatsCalculator::GetAvgFlowTableUsage(uint8_t tableId) const { return 0; } - return std::round(static_cast(GetEwmaFlowTableEntries(tableId)) * - 100 / + return std::round(static_cast(GetEwmaFlowTableEntries(tableId)) * 100 / static_cast(m_device->GetFlowTableSize(tableId))); } @@ -307,39 +303,38 @@ OFSwitch13StatsCalculator::NotifyConstructionCompleted() m_wrapper = Create(m_filename, std::ios::out); // Scheduling first update and dump. - Simulator::Schedule(m_timeout, - &OFSwitch13StatsCalculator::DumpStatistics, - this); + Simulator::Schedule(m_timeout, &OFSwitch13StatsCalculator::DumpStatistics, this); // Chain up. Object::NotifyConstructionCompleted(); } void -OFSwitch13StatsCalculator::NotifyDatapathTimeout( - Ptr device) +OFSwitch13StatsCalculator::NotifyDatapathTimeout(Ptr device) { + // clang-format off NS_LOG_FUNCTION(this); NS_ASSERT_MSG(m_device == device, "Invalid device pointer."); - m_ewmaBufferEntries = m_alpha * m_device->GetBufferEntries() + - (1 - m_alpha) * m_ewmaBufferEntries; - m_ewmaCpuLoad = m_alpha * m_device->GetCpuLoad().GetBitRate() + - (1 - m_alpha) * m_ewmaCpuLoad; - m_ewmaSumFlowEntries = m_alpha * m_device->GetSumFlowEntries() + - (1 - m_alpha) * m_ewmaSumFlowEntries; - m_ewmaGroupEntries = m_alpha * m_device->GetGroupTableEntries() + - (1 - m_alpha) * m_ewmaGroupEntries; - m_ewmaMeterEntries = m_alpha * m_device->GetMeterTableEntries() + - (1 - m_alpha) * m_ewmaMeterEntries; - m_ewmaPipelineDelay = m_alpha * m_device->GetPipelineDelay().GetDouble() + - (1 - m_alpha) * m_ewmaPipelineDelay; + m_ewmaBufferEntries = + m_alpha * m_device->GetBufferEntries() + (1 - m_alpha) * m_ewmaBufferEntries; + m_ewmaCpuLoad = + m_alpha * m_device->GetCpuLoad().GetBitRate() + (1 - m_alpha) * m_ewmaCpuLoad; + m_ewmaSumFlowEntries = + m_alpha * m_device->GetSumFlowEntries() + (1 - m_alpha) * m_ewmaSumFlowEntries; + m_ewmaGroupEntries = + m_alpha * m_device->GetGroupTableEntries() + (1 - m_alpha) * m_ewmaGroupEntries; + m_ewmaMeterEntries = + m_alpha * m_device->GetMeterTableEntries() + (1 - m_alpha) * m_ewmaMeterEntries; + m_ewmaPipelineDelay = + m_alpha * m_device->GetPipelineDelay().GetDouble() + (1 - m_alpha) * m_ewmaPipelineDelay; for (size_t i = 0; i < m_device->GetNPipelineTables(); i++) { - m_ewmaFlowEntries.at(i) = m_alpha * m_device->GetFlowTableEntries(i) + - (1 - m_alpha) * m_ewmaFlowEntries.at(i); + m_ewmaFlowEntries.at(i) = + m_alpha * m_device->GetFlowTableEntries(i) + (1 - m_alpha) * m_ewmaFlowEntries.at(i); } + // clang-format on } void @@ -351,8 +346,7 @@ OFSwitch13StatsCalculator::NotifyOverloadDrop(Ptr packet) } void -OFSwitch13StatsCalculator::NotifyMeterDrop(Ptr packet, - uint32_t meterId) +OFSwitch13StatsCalculator::NotifyMeterDrop(Ptr packet, uint32_t meterId) { NS_LOG_FUNCTION(this << packet << meterId); @@ -360,8 +354,7 @@ OFSwitch13StatsCalculator::NotifyMeterDrop(Ptr packet, } void -OFSwitch13StatsCalculator::NotifyTableDrop(Ptr packet, - uint8_t tableId) +OFSwitch13StatsCalculator::NotifyTableDrop(Ptr packet, uint8_t tableId) { NS_LOG_FUNCTION(this << packet << +tableId); @@ -398,39 +391,42 @@ OFSwitch13StatsCalculator::DumpStatistics() uint32_t cpuUsage = 0; if (cpuCapy) { - cpuUsage = std::round(static_cast(cpuLoad) * 100 / - static_cast(cpuCapy)); + cpuUsage = std::round(static_cast(cpuLoad) * 100 / static_cast(cpuCapy)); } // Print statistics to file. - *m_wrapper->GetStream() - << " " << setw(8) << Simulator::Now().GetSeconds() << " " << setw(12) - << static_cast(cpuLoad) / 1000 << " " << setw(7) << cpuUsage - << " " << setw(7) << m_packets << " " << setw(7) - << GetEwmaPipelineDelay().GetMicroSeconds() << " " << setw(7) - << m_loadDrops << " " << setw(7) << m_meterDrops << " " << setw(7) - << m_tableDrops << " " << setw(7) << flowMods - m_lastFlowMods << " " - << setw(7) << meterMods - m_lastMeterMods << " " << setw(7) - << groupMods - m_lastGroupMods << " " << setw(7) - << packetsIn - m_lastPacketsIn << " " << setw(7) - << packetsOut - m_lastPacketsOut << " " << setw(7) - << GetEwmaSumFlowEntries() << " " << setw(7) - << GetAvgActFlowTableUsage() << " " << setw(7) - << GetEwmaMeterTableEntries() << " " << setw(7) - << GetAvgMeterTableUsage() << " " << setw(7) - << GetEwmaGroupTableEntries() << " " << setw(7) - << GetAvgGroupTableUsage() << " " << setw(7) << GetEwmaBufferEntries() - << " " << setw(7) << GetAvgBufferUsage(); + // clang-format off + *m_wrapper->GetStream() << " " << setw(8) << Simulator::Now().GetSeconds() + << " " << setw(12) << static_cast(cpuLoad) / 1000 + << " " << setw(7) << cpuUsage + << " " << setw(7) << m_packets + << " " << setw(7) << GetEwmaPipelineDelay().GetMicroSeconds() + << " " << setw(7) << m_loadDrops + << " " << setw(7) << m_meterDrops + << " " << setw(7) << m_tableDrops + << " " << setw(7) << flowMods - m_lastFlowMods + << " " << setw(7) << meterMods - m_lastMeterMods + << " " << setw(7) << groupMods - m_lastGroupMods + << " " << setw(7) << packetsIn - m_lastPacketsIn + << " " << setw(7) << packetsOut - m_lastPacketsOut + << " " << setw(7) << GetEwmaSumFlowEntries() + << " " << setw(7) << GetAvgActFlowTableUsage() + << " " << setw(7) << GetEwmaMeterTableEntries() + << " " << setw(7) << GetAvgMeterTableUsage() + << " " << setw(7) << GetEwmaGroupTableEntries() + << " " << setw(7) << GetAvgGroupTableUsage() + << " " << setw(7) << GetEwmaBufferEntries() + << " " << setw(7) << GetAvgBufferUsage(); if (m_details) { for (size_t i = 0; i < m_device->GetNPipelineTables(); i++) { - *m_wrapper->GetStream() - << " " << setw(7) << GetEwmaFlowTableEntries(i) << " " - << setw(7) << GetAvgFlowTableUsage(i); + *m_wrapper->GetStream() << " " << setw(7) << GetEwmaFlowTableEntries(i) + << " " << setw(7) << GetAvgFlowTableUsage(i); } } + // clang-format on *m_wrapper->GetStream() << std::endl; @@ -448,9 +444,7 @@ OFSwitch13StatsCalculator::DumpStatistics() // Scheduling next update. m_lastUpdate = Simulator::Now(); - Simulator::Schedule(m_timeout, - &OFSwitch13StatsCalculator::DumpStatistics, - this); + Simulator::Schedule(m_timeout, &OFSwitch13StatsCalculator::DumpStatistics, this); } } // Namespace ns3 diff --git a/helper/ofswitch13-stats-calculator.h b/helper/ofswitch13-stats-calculator.h index 0ded199..6c16b08 100644 --- a/helper/ofswitch13-stats-calculator.h +++ b/helper/ofswitch13-stats-calculator.h @@ -72,8 +72,8 @@ class OFSwitch13StatsCalculator : public Object static TypeId GetTypeId(); /** - * Hook switch device trace sources to internal stats calculator trace - * sinks. \param device The OpenFlow switch device to monitor. + * Hook switch device trace sources to stats calculator trace sinks. + * \param device The OpenFlow switch device to monitor. */ void HookSinks(Ptr device); @@ -154,8 +154,8 @@ class OFSwitch13StatsCalculator : public Object void NotifyPipelinePacket(Ptr packet); /** - * Read statistics from switch, update internal counters, - * and dump data into output file. + * Read statistics from switch, update internal counters, and dump data into + * output file. */ void DumpStatistics(); diff --git a/model/ofswitch13-controller.cc b/model/ofswitch13-controller.cc index 89fd00d..651c9e3 100644 --- a/model/ofswitch13-controller.cc +++ b/model/ofswitch13-controller.cc @@ -47,15 +47,14 @@ OFSwitch13Controller::~OFSwitch13Controller() TypeId OFSwitch13Controller::GetTypeId() { - static TypeId tid = - TypeId("ns3::OFSwitch13Controller") - .SetParent() - .SetGroupName("OFSwitch13") - .AddAttribute("Port", - "Port number to listen for incoming packets.", - UintegerValue(6653), - MakeUintegerAccessor(&OFSwitch13Controller::m_port), - MakeUintegerChecker()); + static TypeId tid = TypeId("ns3::OFSwitch13Controller") + .SetParent() + .SetGroupName("OFSwitch13") + .AddAttribute("Port", + "Port number to listen for incoming packets.", + UintegerValue(6653), + MakeUintegerAccessor(&OFSwitch13Controller::m_port), + MakeUintegerChecker()); return tid; } @@ -83,7 +82,7 @@ OFSwitch13Controller::DpctlExecute(uint64_t dpId, const std::string textCmd) if (!swtch) { // Save this command for further execution after handshake procedure. - NS_LOG_DEBUG("Schedulling command for an unregistered switch."); + NS_LOG_DEBUG("Scheduling command for an unregistered switch."); auto it = m_commandsMap.find(dpId); if (it == m_commandsMap.end()) { @@ -116,8 +115,7 @@ OFSwitch13Controller::DpctlExecute(uint64_t dpId, const std::string textCmd) } void -OFSwitch13Controller::DpctlSendAndPrint(struct vconn* vconn, - struct ofl_msg_header* msg) +OFSwitch13Controller::DpctlSendAndPrint(struct vconn* vconn, struct ofl_msg_header* msg) { NS_LOG_FUNCTION_NOARGS(); @@ -139,12 +137,10 @@ OFSwitch13Controller::StartApplication() m_serverSocket->Listen(); // Setting socket callbacks - m_serverSocket->SetAcceptCallback( - MakeCallback(&OFSwitch13Controller::SocketRequest, this), - MakeCallback(&OFSwitch13Controller::SocketAccept, this)); - m_serverSocket->SetCloseCallbacks( - MakeCallback(&OFSwitch13Controller::SocketPeerClose, this), - MakeCallback(&OFSwitch13Controller::SocketPeerError, this)); + m_serverSocket->SetAcceptCallback(MakeCallback(&OFSwitch13Controller::SocketRequest, this), + MakeCallback(&OFSwitch13Controller::SocketAccept, this)); + m_serverSocket->SetCloseCallbacks(MakeCallback(&OFSwitch13Controller::SocketPeerClose, this), + MakeCallback(&OFSwitch13Controller::SocketPeerError, this)); } void @@ -204,8 +200,8 @@ OFSwitch13Controller::SendToSwitch(Ptr swtch, NS_LOG_FUNCTION(this << swtch); char* msgStr = ofl_msg_to_string(msg, nullptr); - NS_LOG_DEBUG("TX to switch " << swtch->GetIpv4() << " [dp " - << swtch->GetDpId() << "]: " << msgStr); + NS_LOG_DEBUG("TX to switch " << swtch->GetIpv4() << " [dp " << swtch->GetDpId() + << "]: " << msgStr); free(msgStr); // Set the transaction ID only for unknown values @@ -219,8 +215,7 @@ OFSwitch13Controller::SendToSwitch(Ptr swtch, } void -OFSwitch13Controller::SendEchoRequest(Ptr swtch, - size_t payloadSize) +OFSwitch13Controller::SendEchoRequest(Ptr swtch, size_t payloadSize) { NS_LOG_FUNCTION(this << swtch); @@ -306,14 +301,13 @@ OFSwitch13Controller::HandleEchoReply(struct ofl_msg_echo* msg, auto it = m_echoMap.find(xid); if (it == m_echoMap.end()) { - NS_LOG_WARN("Echo response for unknonw echo request."); + NS_LOG_WARN("Echo response for unknown echo request."); } else { it->second.m_waiting = false; it->second.m_recv = Simulator::Now(); - NS_LOG_INFO("Echo reply from " << it->second.m_swtch->GetIpv4() - << " with RTT " + NS_LOG_INFO("Echo reply from " << it->second.m_swtch->GetIpv4() << " with RTT " << it->second.GetRtt().As(Time::MS)); m_echoMap.erase(it); } @@ -332,7 +326,7 @@ OFSwitch13Controller::HandleBarrierReply(struct ofl_msg_header* msg, auto it = m_barrierMap.find(xid); if (it == m_barrierMap.end()) { - NS_LOG_WARN("Barrier response for unknonw barrier request."); + NS_LOG_WARN("Barrier response for unknown barrier request."); } else { @@ -475,10 +469,9 @@ OFSwitch13Controller::HandleAsyncReply(struct ofl_msg_async_config* msg, } ofl_err -OFSwitch13Controller::HandleMultipartReply( - struct ofl_msg_multipart_reply_header* msg, - Ptr swtch, - uint32_t xid) +OFSwitch13Controller::HandleMultipartReply(struct ofl_msg_multipart_reply_header* msg, + Ptr swtch, + uint32_t xid) { NS_LOG_FUNCTION(this << swtch << xid); @@ -498,10 +491,9 @@ OFSwitch13Controller::HandleRoleReply(struct ofl_msg_role_request* msg, } ofl_err -OFSwitch13Controller::HandleQueueGetConfigReply( - struct ofl_msg_queue_get_config_reply* msg, - Ptr swtch, - uint32_t xid) +OFSwitch13Controller::HandleQueueGetConfigReply(struct ofl_msg_queue_get_config_reply* msg, + Ptr swtch, + uint32_t xid) { NS_LOG_FUNCTION(this << swtch << xid); @@ -541,14 +533,10 @@ OFSwitch13Controller::HandleSwitchMsg(struct ofl_msg_header* msg, return HandleError((struct ofl_msg_error*)msg, swtch, xid); case OFPT_FEATURES_REPLY: - return HandleFeaturesReply((struct ofl_msg_features_reply*)msg, - swtch, - xid); + return HandleFeaturesReply((struct ofl_msg_features_reply*)msg, swtch, xid); case OFPT_GET_CONFIG_REPLY: - return HandleGetConfigReply((struct ofl_msg_get_config_reply*)msg, - swtch, - xid); + return HandleGetConfigReply((struct ofl_msg_get_config_reply*)msg, swtch, xid); case OFPT_FLOW_REMOVED: return HandleFlowRemoved((struct ofl_msg_flow_removed*)msg, swtch, xid); @@ -560,18 +548,13 @@ OFSwitch13Controller::HandleSwitchMsg(struct ofl_msg_header* msg, return HandleAsyncReply((struct ofl_msg_async_config*)msg, swtch, xid); case OFPT_MULTIPART_REPLY: - return HandleMultipartReply((struct ofl_msg_multipart_reply_header*)msg, - swtch, - xid); + return HandleMultipartReply((struct ofl_msg_multipart_reply_header*)msg, swtch, xid); case OFPT_ROLE_REPLY: return HandleRoleReply((struct ofl_msg_role_request*)msg, swtch, xid); case OFPT_QUEUE_GET_CONFIG_REPLY: - return HandleQueueGetConfigReply( - (struct ofl_msg_queue_get_config_reply*)msg, - swtch, - xid); + return HandleQueueGetConfigReply((struct ofl_msg_queue_get_config_reply*)msg, swtch, xid); case OFPT_EXPERIMENTER: default: @@ -590,18 +573,15 @@ OFSwitch13Controller::ReceiveFromSwitch(Ptr packet, Address from) // Get the openflow buffer, unpack the message and send to message handler struct ofpbuf* buffer = BufferFromPacket(packet, packet->GetSize()); - error = ofl_msg_unpack((uint8_t*)buffer->data, - buffer->size, - &msg, - &xid, - nullptr); + error = ofl_msg_unpack((uint8_t*)buffer->data, buffer->size, &msg, &xid, nullptr); if (!error) { Ptr swtch = GetRemoteSwitch(from); char* msgStr = ofl_msg_to_string(msg, nullptr); - NS_LOG_DEBUG("RX from switch " << swtch->GetIpv4() << " [dp " - << swtch->GetDpId() << "]: " << msgStr); + Ipv4Address swIpv4 = swtch->GetIpv4(); + uint64_t swDpId = swtch->GetDpId(); + NS_LOG_DEBUG("RX from switch " << swIpv4 << " [dp " << swDpId << "]: " << msgStr); free(msgStr); error = HandleSwitchMsg(msg, swtch, xid); diff --git a/model/ofswitch13-controller.h b/model/ofswitch13-controller.h index 179e473..81eb7c3 100644 --- a/model/ofswitch13-controller.h +++ b/model/ofswitch13-controller.h @@ -34,12 +34,12 @@ namespace ns3 /** * \ingroup ofswitch13 * OpenFlow 1.3 controller base class that can handle a collection of OpenFlow - * switches and provides the basic functionalities for controller - * implementation. For constructing OpenFlow configuration messages and sending - * them to the switches, this class uses the DpctlCommand function, which - * relies on command-line syntax from the dpctl utility. For OpenFlow messages - * coming from the switches, this class provides a collection of internal - * handlers to deal with the different types of messages. + * switches and provides the basic functionality for controller implementation. + * For constructing OpenFlow configuration messages and sending them to the + * switches, this class uses the DpctlCommand function, which relies on + * command-line syntax from the dpctl utility. For OpenFlow messages coming from + * the switches, this class provides a collection of internal handlers to deal + * with the different types of messages. */ class OFSwitch13Controller : public Application { @@ -80,11 +80,10 @@ class OFSwitch13Controller : public Application Address m_address; //!< Switch connection address. Ptr m_ctrlApp; //!< Controller application. uint64_t m_dpId; //!< OpenFlow datapath ID. - enum ofp_controller_role m_role; //!< Controller role over switch. + enum ofp_controller_role m_role; //!< Controller role over switch. /** - * Switch features informed to the controller during handshake - * procedure. + * Switch features informed to controller during handshake procedure. */ //\{ uint32_t m_nBuffers; //!< Max packets buffered at once. @@ -184,16 +183,15 @@ class OFSwitch13Controller : public Application int DpctlExecute(uint64_t dpId, const std::string textCmd); /** - * Overriding BOFUSS dpctl_send_and_print and - * dpctl_transact_and_print weak functions from utilities/dpctl.c. Send a - * message from controller to switch. + * Overriding BOFUSS dpctl_send_and_print() and dpctl_transact_and_print() + * weak functions from utilities/dpctl.c. Send a message from controller to + * switch. * \param vconn The RemoteSwitch pointer, sent from controller to - * dpctl_exec_ns3_command function and get back here to proper identify the - * controller object. + * dpctl_exec_ns3_command() function and get back here to proper identify + * the controller object. * \param msg The OFLib message to send. */ - static void DpctlSendAndPrint(struct vconn* vconn, - struct ofl_msg_header* msg); + static void DpctlSendAndPrint(struct vconn* vconn, struct ofl_msg_header* msg); protected: // inherited from Application @@ -206,10 +204,11 @@ class OFSwitch13Controller : public Application uint32_t GetNextXid(); /** - * Function invoked after a successfully handshake procedure between - * this controller and a remote switch. Derived classes can override this + * Function invoked after a successfully handshake procedure between this + * controller and a remote switch. Derived classes can override this * function to implement any relevant logic, as sending initial - * configuration messages to the switch. \param swtch The remote switch. + * configuration messages to the switch. + * \param swtch The remote switch. */ virtual void HandshakeSuccessful(Ptr swtch); @@ -227,23 +226,22 @@ class OFSwitch13Controller : public Application * \param xid The transaction id to use. * \return 0 if everything's ok, otherwise an error number. */ - int SendToSwitch(Ptr swtch, - struct ofl_msg_header* msg, - uint32_t xid = 0); + int SendToSwitch(Ptr swtch, struct ofl_msg_header* msg, uint32_t xid = 0); /** - * Send an echo request message to switch, and wait for a non-blocking - * reply. \param swtch The remote switch to receive the message. \param - * payloadSize The ammount of dummy bytes in echo message. + * Send an echo request message to switch and wait for a non-blocking reply. + * \param swtch The remote switch to receive the message. + * \param payloadSize The amount of dummy bytes in echo message. */ void SendEchoRequest(Ptr swtch, size_t payloadSize = 0); /** - * Send a barrier request message to switch, and wait for a non-blocking + * Send a barrier request message to switch and wait for a non-blocking * reply. Note that current OpenFlow device implementation is * single-threaded and messages are processed in the same order that are * received from the controller, so a barrier request will simply be replied - * by the switch. \param swtch The remote switch to receive the message. + * by the switch. + * \param swtch The remote switch to receive the message. */ void SendBarrierRequest(Ptr swtch); @@ -256,7 +254,7 @@ class OFSwitch13Controller : public Application * just free the received message and returns 0. Derived controllers can * override them as they wish to implement the desired control logic. * - * Note that for HandleMultipartReply there are several types of multipart + * Note that for HandleMultipartReply() there are several types of multipart * messages. Derived controllers can filter by the type they wish. * * \attention Handlers \em MUST free received msg when everything is ok. @@ -266,6 +264,7 @@ class OFSwitch13Controller : public Application * \return 0 if everything's ok, otherwise an error number. */ //\{ + // clang-format off ofl_err HandleEchoRequest(struct ofl_msg_echo* msg, Ptr swtch, uint32_t xid); @@ -310,33 +309,30 @@ class OFSwitch13Controller : public Application Ptr swtch, uint32_t xid); - virtual ofl_err HandleMultipartReply( - struct ofl_msg_multipart_reply_header* msg, - Ptr swtch, - uint32_t xid); + virtual ofl_err HandleMultipartReply(struct ofl_msg_multipart_reply_header* msg, + Ptr swtch, + uint32_t xid); virtual ofl_err HandleRoleReply(struct ofl_msg_role_request* msg, Ptr swtch, uint32_t xid); - virtual ofl_err HandleQueueGetConfigReply( - struct ofl_msg_queue_get_config_reply* msg, - Ptr swtch, - uint32_t xid); + virtual ofl_err HandleQueueGetConfigReply(struct ofl_msg_queue_get_config_reply* msg, + Ptr swtch, + uint32_t xid); + // clang-format on //\} private: /** - * Called when an OpenFlow message is received from a switch. - * Dispatches control messages to appropriate handler functions. + * Called when an OpenFlow message is received from a switch. Dispatch + * control messages to appropriate handler functions. * \param msg The OFLib message received. * \param swtch The remote switch the message was received from. * \param xid The transaction id. * \return 0 if everything's ok, otherwise an error number. */ - ofl_err HandleSwitchMsg(struct ofl_msg_header* msg, - Ptr swtch, - uint32_t xid); + ofl_err HandleSwitchMsg(struct ofl_msg_header* msg, Ptr swtch, uint32_t xid); /** * Receive an OpenFlow packet from switch. diff --git a/model/ofswitch13-device.cc b/model/ofswitch13-device.cc index b821be9..fcd5c41 100644 --- a/model/ofswitch13-device.cc +++ b/model/ofswitch13-device.cc @@ -24,10 +24,10 @@ #include #undef NS_LOG_APPEND_CONTEXT -#define NS_LOG_APPEND_CONTEXT \ - if (m_dpId) \ - { \ - std::clog << "[dp " << m_dpId << "] "; \ +#define NS_LOG_APPEND_CONTEXT \ + if (m_dpId) \ + { \ + std::clog << "[dp " << m_dpId << "] "; \ } namespace ns3 @@ -74,139 +74,117 @@ OFSwitch13Device::GetTypeId() .SetParent() .SetGroupName("OFSwitch13") .AddConstructor() - .AddAttribute( - "CpuCapacity", - "CPU processing capacity (in terms of throughput).", - DataRateValue(DataRate("100Gb/s")), - MakeDataRateAccessor(&OFSwitch13Device::m_cpuCapacity), - MakeDataRateChecker()) + .AddAttribute("CpuCapacity", + "CPU processing capacity (in terms of throughput).", + DataRateValue(DataRate("100Gb/s")), + MakeDataRateAccessor(&OFSwitch13Device::m_cpuCapacity), + MakeDataRateChecker()) .AddAttribute("DatapathId", "The unique identification of this OpenFlow switch.", TypeId::ATTR_GET, UintegerValue(0), MakeUintegerAccessor(&OFSwitch13Device::m_dpId), MakeUintegerChecker()) - .AddAttribute( - "FlowTableSize", - "The maximum number of entries allowed on each flow table.", - UintegerValue(FLOW_TABLE_MAX_ENTRIES), - MakeUintegerAccessor(&OFSwitch13Device::SetDftFlowTableSize, - &OFSwitch13Device::GetDftFlowTableSize), - MakeUintegerChecker(0, FLOW_TABLE_MAX_ENTRIES)) - .AddAttribute( - "GroupTableSize", - "The maximum number of entries allowed on group table.", - UintegerValue(GROUP_TABLE_MAX_ENTRIES), - MakeUintegerAccessor(&OFSwitch13Device::SetGroupTableSize, - &OFSwitch13Device::GetGroupTableSize), - MakeUintegerChecker(0, GROUP_TABLE_MAX_ENTRIES)) - .AddAttribute( - "MeterTableSize", - "The maximum number of entries allowed on meter table.", - UintegerValue(METER_TABLE_MAX_ENTRIES), - MakeUintegerAccessor(&OFSwitch13Device::SetMeterTableSize, - &OFSwitch13Device::GetMeterTableSize), - MakeUintegerChecker(0, METER_TABLE_MAX_ENTRIES)) - .AddAttribute( - "PipelineTables", - "The number of pipeline flow tables.", - TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, - UintegerValue(64), - MakeUintegerAccessor(&OFSwitch13Device::m_numPipeTabs), - MakeUintegerChecker(1, (OFPTT_MAX + 1))) + .AddAttribute("FlowTableSize", + "The maximum number of entries allowed on each flow table.", + UintegerValue(FLOW_TABLE_MAX_ENTRIES), + MakeUintegerAccessor(&OFSwitch13Device::SetDftFlowTableSize, + &OFSwitch13Device::GetDftFlowTableSize), + MakeUintegerChecker(0, FLOW_TABLE_MAX_ENTRIES)) + .AddAttribute("GroupTableSize", + "The maximum number of entries allowed on group table.", + UintegerValue(GROUP_TABLE_MAX_ENTRIES), + MakeUintegerAccessor(&OFSwitch13Device::SetGroupTableSize, + &OFSwitch13Device::GetGroupTableSize), + MakeUintegerChecker(0, GROUP_TABLE_MAX_ENTRIES)) + .AddAttribute("MeterTableSize", + "The maximum number of entries allowed on meter table.", + UintegerValue(METER_TABLE_MAX_ENTRIES), + MakeUintegerAccessor(&OFSwitch13Device::SetMeterTableSize, + &OFSwitch13Device::GetMeterTableSize), + MakeUintegerChecker(0, METER_TABLE_MAX_ENTRIES)) + .AddAttribute("PipelineTables", + "The number of pipeline flow tables.", + TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, + UintegerValue(64), + MakeUintegerAccessor(&OFSwitch13Device::m_numPipeTabs), + MakeUintegerChecker(1, (OFPTT_MAX + 1))) .AddAttribute("PortList", "The list of ports associated to this switch.", ObjectVectorValue(), MakeObjectVectorAccessor(&OFSwitch13Device::m_ports), MakeObjectVectorChecker()) - .AddAttribute( - "TcamDelay", - "Average time to perform a TCAM operation in pipeline.", - TimeValue(MicroSeconds(20)), - MakeTimeAccessor(&OFSwitch13Device::m_tcamDelay), - MakeTimeChecker(Time(0))) - .AddAttribute( - "TimeoutInterval", - "The interval between timeout operations on datapath.", - TimeValue(MilliSeconds(100)), - MakeTimeAccessor(&OFSwitch13Device::m_timeout), - MakeTimeChecker(MilliSeconds(1), MilliSeconds(1000))) - - .AddTraceSource( - "BufferExpire", - "Trace source indicating an expired packet in buffer.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_bufferExpireTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "BufferRetrieve", - "Trace source indicating a packet retrieved from buffer.", - MakeTraceSourceAccessor( - &OFSwitch13Device::m_bufferRetrieveTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "BufferSave", - "Trace source indicating a packet saved into buffer.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_bufferSaveTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "MeterDrop", - "Trace source indicating a packet dropped by meter band.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_meterDropTrace), - "ns3::OFSwitch13Device::MeterDropTracedCallback") - .AddTraceSource( - "OverloadDrop", - "Trace source indicating a packet dropped by CPU " - "overloaded processing capacity.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_loadDropTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "TableDrop", - "Trace source indicating an unmatched packet dropped by " - "a flow table without a table-miss entry.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_tableDropTrace), - "ns3::OFSwitch13Device::TableDropTracedCallback") - .AddTraceSource( - "PipelinePacket", - "Trace source indicating a packet sent to pipeline.", - MakeTraceSourceAccessor(&OFSwitch13Device::m_pipePacketTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "DatapathTimeout", - "Trace source indicating a datapath timeout operation.", - MakeTraceSourceAccessor( - &OFSwitch13Device::m_datapathTimeoutTrace), - "ns3::OFSwitch13Device::DeviceTracedCallback") - - .AddTraceSource( - "CpuLoad", - "Traced value indicating the avg CPU processing load" - " (periodically updated on datapath timeout operation).", - MakeTraceSourceAccessor(&OFSwitch13Device::m_cpuLoad), - "ns3::TracedValueCallback::DataRate") - .AddTraceSource( - "GroupEntries", - "Traced value indicating the number of group entries" - " (periodically updated on datapath timeout operation).", - MakeTraceSourceAccessor(&OFSwitch13Device::m_groupEntries), - "ns3::TracedValueCallback::Uint32") - .AddTraceSource( - "MeterEntries", - "Traced value indicating the number of meter entries" - " (periodically updated on datapath timeout operation).", - MakeTraceSourceAccessor(&OFSwitch13Device::m_meterEntries), - "ns3::TracedValueCallback::Uint32") - .AddTraceSource( - "PipelineDelay", - "Traced value indicating the avg pipeline lookup delay" - " (periodically updated on datapath timeout operation).", - MakeTraceSourceAccessor(&OFSwitch13Device::m_pipeDelay), - "ns3::TracedValueCallback::Time") - .AddTraceSource( - "SumFlowEntries", - "Traced value indicating the total number of flow entries" - " (periodically updated on datapath timeout operation).", - MakeTraceSourceAccessor(&OFSwitch13Device::m_sumFlowEntries), - "ns3::TracedValueCallback::Uint32"); + .AddAttribute("TcamDelay", + "Average time to perform a TCAM operation in pipeline.", + TimeValue(MicroSeconds(20)), + MakeTimeAccessor(&OFSwitch13Device::m_tcamDelay), + MakeTimeChecker(Time(0))) + .AddAttribute("TimeoutInterval", + "The interval between timeout operations on datapath.", + TimeValue(MilliSeconds(100)), + MakeTimeAccessor(&OFSwitch13Device::m_timeout), + MakeTimeChecker(MilliSeconds(1), MilliSeconds(1000))) + + .AddTraceSource("BufferExpire", + "Trace source indicating an expired packet in buffer.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_bufferExpireTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("BufferRetrieve", + "Trace source indicating a packet retrieved from buffer.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_bufferRetrieveTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("BufferSave", + "Trace source indicating a packet saved into buffer.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_bufferSaveTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("MeterDrop", + "Trace source indicating a packet dropped by meter band.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_meterDropTrace), + "ns3::OFSwitch13Device::MeterDropTracedCallback") + .AddTraceSource("OverloadDrop", + "Trace source indicating a packet dropped by CPU " + "overloaded processing capacity.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_loadDropTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("TableDrop", + "Trace source indicating an unmatched packet dropped by " + "a flow table without a table-miss entry.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_tableDropTrace), + "ns3::OFSwitch13Device::TableDropTracedCallback") + .AddTraceSource("PipelinePacket", + "Trace source indicating a packet sent to pipeline.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_pipePacketTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("DatapathTimeout", + "Trace source indicating a datapath timeout operation.", + MakeTraceSourceAccessor(&OFSwitch13Device::m_datapathTimeoutTrace), + "ns3::OFSwitch13Device::DeviceTracedCallback") + + .AddTraceSource("CpuLoad", + "Traced value indicating the avg CPU processing load" + " (periodically updated on datapath timeout operation).", + MakeTraceSourceAccessor(&OFSwitch13Device::m_cpuLoad), + "ns3::TracedValueCallback::DataRate") + .AddTraceSource("GroupEntries", + "Traced value indicating the number of group entries" + " (periodically updated on datapath timeout operation).", + MakeTraceSourceAccessor(&OFSwitch13Device::m_groupEntries), + "ns3::TracedValueCallback::Uint32") + .AddTraceSource("MeterEntries", + "Traced value indicating the number of meter entries" + " (periodically updated on datapath timeout operation).", + MakeTraceSourceAccessor(&OFSwitch13Device::m_meterEntries), + "ns3::TracedValueCallback::Uint32") + .AddTraceSource("PipelineDelay", + "Traced value indicating the avg pipeline lookup delay" + " (periodically updated on datapath timeout operation).", + MakeTraceSourceAccessor(&OFSwitch13Device::m_pipeDelay), + "ns3::TracedValueCallback::Time") + .AddTraceSource("SumFlowEntries", + "Traced value indicating the total number of flow entries" + " (periodically updated on datapath timeout operation).", + MakeTraceSourceAccessor(&OFSwitch13Device::m_sumFlowEntries), + "ns3::TracedValueCallback::Uint32"); return tid; } @@ -271,8 +249,7 @@ OFSwitch13Device::GetBufferUsage() const { return 0.0; } - return static_cast(GetBufferEntries()) / - static_cast(GetBufferSize()); + return static_cast(GetBufferEntries()) / static_cast(GetBufferSize()); } DataRate @@ -357,8 +334,7 @@ OFSwitch13Device::GetGroupTableUsage() const { return 0.0; } - return static_cast(GetGroupTableEntries()) / - static_cast(GetGroupTableSize()); + return static_cast(GetGroupTableEntries()) / static_cast(GetGroupTableSize()); } uint32_t @@ -381,8 +357,7 @@ OFSwitch13Device::GetMeterTableUsage() const { return 0.0; } - return static_cast(GetMeterTableEntries()) / - static_cast(GetMeterTableSize()); + return static_cast(GetMeterTableEntries()) / static_cast(GetMeterTableSize()); } uint32_t @@ -432,17 +407,14 @@ OFSwitch13Device::AddSwitchPort(Ptr portDevice) NS_LOG_FUNCTION(this << portDevice); NS_LOG_INFO("Adding port addr " << portDevice->GetAddress()); - NS_ABORT_MSG_IF(GetNSwitchPorts() >= DP_MAX_PORTS, - "No more ports allowed."); + NS_ABORT_MSG_IF(GetNSwitchPorts() >= DP_MAX_PORTS, "No more ports allowed."); // Create the OpenFlow port for this device. - Ptr ofPort; - ofPort = CreateObject(m_datapath, portDevice, this); + Ptr ofPort = CreateObject(m_datapath, portDevice, this); // Save port in port list (assert port no and vector index). m_ports.emplace_back(ofPort); - NS_ASSERT((m_ports.size() == ofPort->GetPortNo()) && - (m_ports.size() == m_datapath->ports_num)); + NS_ASSERT((m_ports.size() == ofPort->GetPortNo()) && (m_ports.size() == m_datapath->ports_num)); return ofPort; } @@ -458,9 +430,7 @@ OFSwitch13Device::GetSwitchPort(uint32_t no) const } void -OFSwitch13Device::ReceiveFromSwitchPort(Ptr packet, - uint32_t portNo, - uint64_t tunnelId) +OFSwitch13Device::ReceiveFromSwitchPort(Ptr packet, uint32_t portNo, uint64_t tunnelId) { NS_LOG_FUNCTION(this << packet << portNo << tunnelId); @@ -495,8 +465,7 @@ OFSwitch13Device::StartControllerConnection(Address ctrlAddr) NS_ASSERT(!ctrlAddr.IsInvalid()); NS_ASSERT_MSG(InetSocketAddress::IsMatchingType(ctrlAddr), "Invalid address type (only IPv4 supported by now)."); - NS_ASSERT_MSG(!GetRemoteController(ctrlAddr), - "Controller address already in use."); + NS_ASSERT_MSG(!GetRemoteController(ctrlAddr), "Controller address already in use."); // Start a TCP connection to this target controller. int error = 0; @@ -518,9 +487,8 @@ OFSwitch13Device::StartControllerConnection(Address ctrlAddr) return; } - ctrlSocket->SetConnectCallback( - MakeCallback(&OFSwitch13Device::SocketCtrlSucceeded, this), - MakeCallback(&OFSwitch13Device::SocketCtrlFailed, this)); + ctrlSocket->SetConnectCallback(MakeCallback(&OFSwitch13Device::SocketCtrlSucceeded, this), + MakeCallback(&OFSwitch13Device::SocketCtrlFailed, this)); // Create a RemoteController object for this controller and save it. Ptr remoteCtrl = Create(); @@ -537,15 +505,11 @@ OFSwitch13Device::SendPacketToController(struct pipeline* pl, uint8_t reason) { Ptr dev = OFSwitch13Device::GetDevice(pl->dp->id); - dev->SendPacketInMessage(pkt, - tableId, - reason, - dev->m_datapath->config.miss_send_len); + dev->SendPacketInMessage(pkt, tableId, reason, dev->m_datapath->config.miss_send_len); } int -OFSwitch13Device::SendOpenflowBufferToRemote(struct ofpbuf* buffer, - struct remote* remote) +OFSwitch13Device::SendOpenflowBufferToRemote(struct ofpbuf* buffer, struct remote* remote) { Ptr dev = OFSwitch13Device::GetDevice(remote->dp->id); Ptr packet = PacketFromBuffer(buffer); @@ -584,8 +548,7 @@ OFSwitch13Device::DpActionsOutputPort(struct packet* pkt, case (OFPP_CONTROLLER): { dev->SendPacketInMessage(pkt, pkt->table_id, - pkt->handle_std->table_miss ? OFPR_NO_MATCH - : OFPR_ACTION, + pkt->handle_std->table_miss ? OFPR_NO_MATCH : OFPR_ACTION, maxLength, cookie); break; @@ -623,16 +586,14 @@ OFSwitch13Device::MeterCreatedCallback(struct meter_entry* entry) } void -OFSwitch13Device::MeterDropCallback(struct packet* pkt, - struct meter_entry* entry) +OFSwitch13Device::MeterDropCallback(struct packet* pkt, struct meter_entry* entry) { Ptr dev = OFSwitch13Device::GetDevice(pkt->dp->id); dev->NotifyPacketDroppedByMeter(pkt, entry); } void -OFSwitch13Device::TableDropCallback(struct packet* pkt, - struct flow_table* table) +OFSwitch13Device::TableDropCallback(struct packet* pkt, struct flow_table* table) { Ptr dev = OFSwitch13Device::GetDevice(pkt->dp->id); dev->NotifyPacketDroppedByTable(pkt, table); @@ -722,7 +683,7 @@ OFSwitch13Device::NotifyConstructionCompleted() // Create the datapath. m_datapath = DatapathNew(); - // Set the attribute values again so it can now update the dapatah structs. + // Set the attribute values again so it can now update the datapath structs. SetDftFlowTableSize(GetDftFlowTableSize()); SetGroupTableSize(GetGroupTableSize()); SetMeterTableSize(GetMeterTableSize()); @@ -779,8 +740,7 @@ OFSwitch13Device::DatapathNew() dp->max_queues = PORT_MAX_QUEUES; dp->exp = nullptr; - dp->config.flags = - OFPC_FRAG_NORMAL; // IP fragments with no special handling + dp->config.flags = OFPC_FRAG_NORMAL; // IP fragments with no special handling dp->config.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN; // 128 bytes // BOFUSS callbacks @@ -801,8 +761,7 @@ OFSwitch13Device::SetFlowTableSize(uint8_t tableId, uint32_t value) NS_LOG_FUNCTION(this << tableId << value); NS_ASSERT_MSG(m_datapath, "No datapath defined yet."); - NS_ABORT_MSG_IF(GetFlowTableEntries(tableId) > value, - "Can't reduce table size to this value."); + NS_ABORT_MSG_IF(GetFlowTableEntries(tableId) > value, "Can't reduce table size to this value."); m_datapath->pipeline->tables[tableId]->features->max_entries = value; } @@ -829,8 +788,7 @@ OFSwitch13Device::SetGroupTableSize(uint32_t value) m_groupTabSize = value; if (m_datapath) { - NS_ABORT_MSG_IF(GetGroupTableEntries() > value, - "Can't reduce table size to this value."); + NS_ABORT_MSG_IF(GetGroupTableEntries() > value, "Can't reduce table size to this value."); for (size_t i = 0; i < 4; i++) { m_datapath->groups->features->max_groups[i] = value; @@ -846,8 +804,7 @@ OFSwitch13Device::SetMeterTableSize(uint32_t value) m_meterTabSize = value; if (m_datapath) { - NS_ABORT_MSG_IF(GetMeterTableEntries() > value, - "Can't reduce table size to this value."); + NS_ABORT_MSG_IF(GetMeterTableEntries() > value, "Can't reduce table size to this value."); m_datapath->meters->features->max_meter = value; } } @@ -872,11 +829,9 @@ OFSwitch13Device::DatapathTimeout(struct datapath* dp) // The pipeline delay is estimated as k * log (n), where 'k' is the // m_tcamDelay set to the time for a single TCAM operation, and 'n' is the // current number of entries on all flow tables. - m_pipeDelay = - m_sumFlowEntries < 2U - ? m_tcamDelay - : m_tcamDelay * - (int64_t)ceil(log2(static_cast(m_sumFlowEntries))); + m_pipeDelay = m_sumFlowEntries < 2U + ? m_tcamDelay + : m_tcamDelay * (int64_t)ceil(log2(static_cast(m_sumFlowEntries))); // The CPU load is estimated based on the CPU consumed tokens since last // timeout operation. @@ -885,18 +840,15 @@ OFSwitch13Device::DatapathTimeout(struct datapath* dp) // Refill the pipeline bucket with tokens based on elapsed time // (bucket capacity is set to the number of tokens for an entire second). - Time elapTime = Simulator::Now() - m_lastTimeout; - uint64_t addTokens = m_cpuCapacity.GetBitRate() * elapTime.GetSeconds(); + Time elapsedTime = Simulator::Now() - m_lastTimeout; + uint64_t addTokens = m_cpuCapacity.GetBitRate() * elapsedTime.GetSeconds(); uint64_t maxTokens = m_cpuCapacity.GetBitRate(); m_cpuTokens = std::min(m_cpuTokens + addTokens, maxTokens); dp->last_timeout = time_now(); m_lastTimeout = Simulator::Now(); m_datapathTimeoutTrace(this); - Simulator::Schedule(m_timeout, - &OFSwitch13Device::DatapathTimeout, - this, - m_datapath); + Simulator::Schedule(m_timeout, &OFSwitch13Device::DatapathTimeout, this, m_datapath); } int @@ -938,9 +890,7 @@ OFSwitch13Device::SendPacketInMessage(struct packet* pkt, } bool -OFSwitch13Device::SendToSwitchPort(struct packet* pkt, - uint32_t portNo, - uint32_t queueNo) +OFSwitch13Device::SendToSwitchPort(struct packet* pkt, uint32_t portNo, uint32_t queueNo) { NS_LOG_FUNCTION(this << pkt->ns3_uid << portNo); @@ -992,9 +942,7 @@ OFSwitch13Device::SendToSwitchPort(struct packet* pkt, } void -OFSwitch13Device::SendToPipeline(Ptr packet, - uint32_t portNo, - uint64_t tunnelId) +OFSwitch13Device::SendToPipeline(Ptr packet, uint32_t portNo, uint64_t tunnelId) { NS_LOG_FUNCTION(this << packet << portNo << tunnelId); @@ -1005,8 +953,7 @@ OFSwitch13Device::SendToPipeline(Ptr packet, uint32_t headRoom = 128 + 2; uint32_t bodyRoom = packet->GetSize() + VLAN_ETH_HEADER_LEN; struct ofpbuf* buffer = BufferFromPacket(packet, bodyRoom, headRoom); - struct packet* pkt = - packet_create(m_datapath, portNo, buffer, tunnelId, false); + struct packet* pkt = packet_create(m_datapath, portNo, buffer, tunnelId, false); // Save the ns-3 packet into pipeline structure. Note that we are using a // private packet uid to avoid conflicts with ns3::Packet uid. @@ -1018,8 +965,7 @@ OFSwitch13Device::SendToPipeline(Ptr packet, } int -OFSwitch13Device::SendToController(Ptr packet, - Ptr remoteCtrl) +OFSwitch13Device::SendToController(Ptr packet, Ptr remoteCtrl) { if (!remoteCtrl->m_socket) { @@ -1057,7 +1003,7 @@ OFSwitch13Device::ReceiveFromController(Ptr packet, Address from) // Check for error while unpacking the message. if (error) { - // The BOFUSS librady only unpacks messages that has the same + // The BOFUSS library only unpacks messages that has the same // OFP_VERSION that is supported by the datapath implementation. // However, when an OpenFlow connection is first established, each side // of the connection must immediately send an OFPT_HELLO message with @@ -1163,9 +1109,7 @@ OFSwitch13Device::ReplyWithErrorMessage(ofl_err error, NS_LOG_ERROR("Error processing OpenFlow message. Reply with " << msgStr); free(msgStr); - return dp_send_message(m_datapath, - (struct ofl_msg_header*)&err, - senderCtrl); + return dp_send_message(m_datapath, (struct ofl_msg_header*)&err, senderCtrl); } void @@ -1267,8 +1211,7 @@ OFSwitch13Device::NotifyPacketDestroyed(struct packet* pkt) } // If we got here, this packet must not be valid on the pipeline structure. - NS_ASSERT_MSG((m_pipePkt.IsValid() && !m_pipePkt.HasId(pkt->ns3_uid)) || - !m_pipePkt.IsValid(), + NS_ASSERT_MSG((m_pipePkt.IsValid() && !m_pipePkt.HasId(pkt->ns3_uid)) || !m_pipePkt.IsValid(), "Packet still valid in pipeline."); // This destroyed packet is probably an old packet that was previously saved @@ -1280,30 +1223,26 @@ OFSwitch13Device::NotifyPacketDestroyed(struct packet* pkt) } void -OFSwitch13Device::NotifyPacketDroppedByMeter(struct packet* pkt, - struct meter_entry* entry) +OFSwitch13Device::NotifyPacketDroppedByMeter(struct packet* pkt, struct meter_entry* entry) { NS_LOG_FUNCTION(this << pkt->ns3_uid << entry->stats->meter_id); uint32_t meterId = entry->stats->meter_id; NS_ASSERT_MSG(m_pipePkt.HasId(pkt->ns3_uid), "Invalid packet ID."); - NS_LOG_DEBUG("OpenFlow meter id " << meterId << " dropped packet " - << pkt->ns3_uid); + NS_LOG_DEBUG("OpenFlow meter id " << meterId << " dropped packet " << pkt->ns3_uid); // Fire drop trace source. m_meterDropTrace(m_pipePkt.GetPacket(), meterId); } void -OFSwitch13Device::NotifyPacketDroppedByTable(struct packet* pkt, - struct flow_table* table) +OFSwitch13Device::NotifyPacketDroppedByTable(struct packet* pkt, struct flow_table* table) { NS_LOG_FUNCTION(this << pkt->ns3_uid << table->stats->table_id); uint8_t tableId = table->stats->table_id; NS_ASSERT_MSG(m_pipePkt.HasId(pkt->ns3_uid), "Invalid packet ID."); - NS_LOG_DEBUG("OpenFlow table id " - << +tableId << " dropped unmatched packet " << pkt->ns3_uid); + NS_LOG_DEBUG("OpenFlow table id " << +tableId << " dropped unmatched packet " << pkt->ns3_uid); // Fire drop trace source. m_tableDropTrace(m_pipePkt.GetPacket(), tableId); diff --git a/model/ofswitch13-device.h b/model/ofswitch13-device.h index 4c46917..0ea40d8 100644 --- a/model/ofswitch13-device.h +++ b/model/ofswitch13-device.h @@ -40,15 +40,15 @@ class OFSwitch13Port; * * An OpenFlow 1.3 device that switches multiple CSMA segments via OpenFlow * protocol. It takes a collection of ports, each one associated with a ns-3 - * underlying CsmaNetDevice. The device acts as the intermediary between the + * underlying CsmaNetDevice. The device acts as the intermediate between the * ports, receiving a packet from one port and forwarding it to another. * * The OpenFlow switch datapath implementation (flow tables, group table, and - * meter table) is provided by the BOFUSS library. For this reason, - * packets entering the switch are sent to the library for OpenFlow pipeline - * processing before being forwarded to the correct output port(s). OpenFlow - * messages received from the controller are also sent to the library for - * datapath configuration. + * meter table) is provided by the BOFUSS library. For this reason, packets + * entering the switch are sent to the library for OpenFlow pipeline processing + * before being forwarded to the correct output port(s). OpenFlow messages + * received from the controller are also sent to the library for datapath + * configuration. */ class OFSwitch13Device : public Object { @@ -80,7 +80,7 @@ class OFSwitch13Device : public Object * the ID for each packet copy (notified by the clone callback). Note that * only one packet can be in pipeline at a time, but the packet can have * multiple internal copies (each one will receive an unique packet ID), and - * can also be saved into buffer for latter usage. + * can also be saved into buffer for later usage. */ struct PipelinePacket { @@ -98,7 +98,7 @@ class OFSwitch13Device : public Object /** \return The packet pointer. */ Ptr GetPacket() const; - /** Invalidate packet metatada.*/ + /** Invalidate packet metadata. */ void Invalidate(); /** @@ -150,7 +150,7 @@ class OFSwitch13Device : public Object uint64_t GetDatapathId() const; /** - * Alias for the GetDatapathId () method. + * Alias for the GetDatapathId() method. * \return The datapath ID. */ uint64_t GetDpId() const; @@ -210,8 +210,9 @@ class OFSwitch13Device : public Object * device. The current implementation only supports CsmaNetDevice (for * physical ports) or VirtualNetDevice (for logical ports). Keep in mind * that an OpenFlow switch expects to receive packets with Ethernet header - * from port devices). \param portDevice The NetDevice port to add. \return - * The OFSwitch13Port created. + * from port devices). + * \param portDevice The NetDevice port to add. + * \return The OFSwitch13Port created. */ Ptr AddSwitchPort(Ptr portDevice); @@ -224,13 +225,12 @@ class OFSwitch13Device : public Object /** * Called when a packet is received on one of the switch's ports. This - * method will schedule the packet for OpenFlow pipeline. \param packet The - * packet. \param portNo The switch input port number. \param tunnelId The - * metadata associated with a logical port. + * method will schedule the packet for OpenFlow pipeline. + * \param packet The packet. + * \param portNo The switch input port number. + * \param tunnelId The metadata associated with a logical port. */ - void ReceiveFromSwitchPort(Ptr packet, - uint32_t portNo, - uint64_t tunnelId = 0); + void ReceiveFromSwitchPort(Ptr packet, uint32_t portNo, uint64_t tunnelId = 0); /** * Starts the TCP connection between this switch and the target controller @@ -240,8 +240,8 @@ class OFSwitch13Device : public Object void StartControllerConnection(Address ctrlAddr); /** - * Overriding BOFUSS send_packet_to_controller weak function - * from udatapath/pipeline.c. Sends the given packet to controller(s) in a + * Overriding BOFUSS send_packet_to_controller weak function from + * udatapath/pipeline.c. Sends the given packet to controller(s) in a * packet_in message. * \internal * This function relies on the global map that stores OpenFlow devices to @@ -267,14 +267,13 @@ class OFSwitch13Device : public Object * \param remote The remote controller connection information. * \return 0 if everything's ok, otherwise an error number. */ - static int SendOpenflowBufferToRemote(struct ofpbuf* buffer, - struct remote* remote); + static int SendOpenflowBufferToRemote(struct ofpbuf* buffer, struct remote* remote); /** * Overriding BOFUSS dp_actions_output_port weak function from * udatapath/dp_actions.c. Outputs a datapath packet on switch port. This - * code is nearly the same on ofsoftswitch, but it gets the openflow device - * from datapath id and uses member functions to send the packet over ns3 + * code is nearly the same on BOFUSS, but it gets the openflow device from + * datapath id and uses member functions to send the packet over ns3 * structures. * \internal * This function relies on the global map that stores OpenFlow devices to @@ -302,12 +301,12 @@ class OFSwitch13Device : public Object * \param pkt The original internal packet. * \param entry The meter entry that dropped the packet. */ - static void MeterDropCallback(struct packet* pkt, - struct meter_entry* entry); + static void MeterDropCallback(struct packet* pkt, struct meter_entry* entry); /** * Callback fired when an unmatched packet is dropped by a flow table - * without a table-miss entry. \param pkt The original internal packet. + * without a table-miss entry. + * \param pkt The original internal packet. * \param table The flow table that dropped the packet. */ static void TableDropCallback(struct packet* pkt, struct flow_table* table); @@ -350,8 +349,7 @@ class OFSwitch13Device : public Object * \param packet The dropped packet. * \param meterId The meter entry ID that dropped the packet. */ - typedef void (*MeterDropTracedCallback)(Ptr packet, - uint32_t meterId); + typedef void (*MeterDropTracedCallback)(Ptr packet, uint32_t meterId); /** * TracedCallback signature for unmatched packets dropped by flow tables @@ -359,12 +357,11 @@ class OFSwitch13Device : public Object * \param packet The dropped packet. * \param tableId The flow table ID that dropped the packet. */ - typedef void (*TableDropTracedCallback)(Ptr packet, - uint8_t tableId); + typedef void (*TableDropTracedCallback)(Ptr packet, uint8_t tableId); /** * TracedCallback signature for OpenFlow switch device. - * \param deve The OpenFlow switch device pointer. + * \param dev The OpenFlow switch device pointer. */ typedef void (*DeviceTracedCallback)(Ptr dev); @@ -379,7 +376,7 @@ class OFSwitch13Device : public Object /** * Creates a new datapath. * \return The created datapath. - * \see ofsoftswitch dp_new () at udatapath/datapath.c + * \see BOFUSS dp_new() at udatapath/datapath.c */ struct datapath* DatapathNew(); @@ -397,9 +394,9 @@ class OFSwitch13Device : public Object /** * Check if any flow in any table is timed out and update port status. This - * method reschedules itself at every m_timout interval, to constantly check + * method schedules itself at every m_timout interval, to periodically check * the pipeline for timed out flow entries and update port status. - * \see BOFUSS function pipeline_timeout () at udatapath/pipeline.c + * \see BOFUSS function pipeline_timeout() at udatapath/pipeline.c * \param dp The datapath. */ void DatapathTimeout(struct datapath* dp); @@ -421,17 +418,15 @@ class OFSwitch13Device : public Object uint64_t cookie = 0); /** - * Send a message over a specific switch port. Check port configuration, - * get the ns-3 packet and send the packet over the proper OpenFlow port. - * \see DpActionsOutputPort (). + * Send a message over a specific switch port. Check port configuration, get + * the ns-3 packet and send the packet over the proper OpenFlow port. + * \see DpActionsOutputPort(). * \param pkt The internal packet to send. * \param portNo The port number. * \param queueNo The queue number. * \return True if success, false otherwise. */ - bool SendToSwitchPort(struct packet* pkt, - uint32_t portNo, - uint32_t queueNo = 0); + bool SendToSwitchPort(struct packet* pkt, uint32_t portNo, uint32_t queueNo = 0); /** * Send the packet to the OpenFlow BOFUSS pipeline. @@ -439,26 +434,23 @@ class OFSwitch13Device : public Object * \param portNo The switch input port number. * \param tunnelId The metadata associated with a logical port. */ - void SendToPipeline(Ptr packet, - uint32_t portNo, - uint64_t tunnelId = 0); + void SendToPipeline(Ptr packet, uint32_t portNo, uint64_t tunnelId = 0); /** * Send a packet to the controller node. - * \see SendOpenflowBufferToRemote (). + * \see SendOpenflowBufferToRemote(). * \attention Don't use this method to directly send messages to controller. - * Use dp_send_message () instead, as it deals with multiple connections and + * Use dp_send_message() instead, as it deals with multiple connections and * check async config. * \param packet The ns-3 packet to send. * \param remoteCtrl The remote controller object to send the packet. * \return 0 if everything's ok, otherwise an error number. */ - int SendToController(Ptr packet, - Ptr remoteCtrl); + int SendToController(Ptr packet, Ptr remoteCtrl); /** * Receive an OpenFlow packet from controller. - * \see remote_rconn_run () at udatapath/datapath.c. + * \see remote_rconn_run() at udatapath/datapath.c. * \param packet The packet with the OpenFlow message. * \param from The packet sender address. */ @@ -473,9 +465,7 @@ class OFSwitch13Device : public Object * \param senderCtrl The origin of a received OpenFlow message. * \return 0 if everything's ok, otherwise an error number. */ - int ReplyWithErrorMessage(ofl_err error, - struct ofpbuf* buffer, - struct sender* senderCtrl); + int ReplyWithErrorMessage(ofl_err error, struct ofpbuf* buffer, struct sender* senderCtrl); /** * Socket callback fired when a TCP connection to controller succeed. @@ -516,8 +506,7 @@ class OFSwitch13Device : public Object * \param pkt The BOFUSS packet. * \param entry The meter entry that dropped the packet. */ - void NotifyPacketDroppedByMeter(struct packet* pkt, - struct meter_entry* entry); + void NotifyPacketDroppedByMeter(struct packet* pkt, struct meter_entry* entry); /** * Notify this device of an unmatched packet dropped by OpenFlow flow table @@ -525,20 +514,20 @@ class OFSwitch13Device : public Object * \param pkt The BOFUSS packet. * \param table The flow table that dropped the packet. */ - void NotifyPacketDroppedByTable(struct packet* pkt, - struct flow_table* table); + void NotifyPacketDroppedByTable(struct packet* pkt, struct flow_table* table); /** * Notify this device of a packet saved into buffer. This method will get - * the ns-3 packet in pipeline and save into buffer map. \param packetId The - * ns-3 packet id. \param timeout The buffer timeout. + * the ns-3 packet in pipeline and save into buffer map. + * \param packetId The ns-3 packet id. + * \param timeout The buffer timeout. */ void BufferPacketSave(uint64_t packetId, uint64_t timeout); /** * Notify this device of a packet retrieved from buffer. This method will - * get the ns-3 packet from buffer map and put it back into pipeline. \param - * packetId The ns-3 packet id. + * get the ns-3 packet from buffer map and put it back into pipeline. + * \param packetId The ns-3 packet id. */ void BufferPacketRetrieve(uint64_t packetId); @@ -553,29 +542,26 @@ class OFSwitch13Device : public Object * \param socket The connection socket. * \return The remote controller. */ - Ptr GetRemoteController( - Ptr socket); + Ptr GetRemoteController(Ptr socket); /** * Get the remote controller for this address. * \param address The socket address. * \return The remote controller. */ - Ptr GetRemoteController( - Address address); + Ptr GetRemoteController(Address address); /** * Get the remote controller for this BOFUSS remote pointer. * \param remote The BOFUSS remote pointer. * \return The remote controller. */ - Ptr GetRemoteController( - struct remote* remote); + Ptr GetRemoteController(struct remote* remote); /** * Increase the global packet ID counter and return a new packet ID. This ID - * is different from the internal ns3::Packet::GetUid (), as we need an - * unique value even for fragmented or brodcast packets. Its usage is + * is different from the internal ns3::Packet::GetUid(), as we need an + * unique value even for fragmented or broadcast packets. Its usage is * restricted to this device. * \return New unique ID for this packet. */ diff --git a/model/ofswitch13-interface.cc b/model/ofswitch13-interface.cc index 2a037fb..49ed5de 100644 --- a/model/ofswitch13-interface.cc +++ b/model/ofswitch13-interface.cc @@ -133,10 +133,7 @@ time_msec(void) /** Overriding BOFUSS weak functions using static member functions. */ void -send_packet_to_controller(struct pipeline* pl, - struct packet* pkt, - uint8_t table_id, - uint8_t reason) +send_packet_to_controller(struct pipeline* pl, struct packet* pkt, uint8_t table_id, uint8_t reason) { return OFSwitch13Device::SendPacketToController(pl, pkt, table_id, reason); } @@ -154,11 +151,7 @@ dp_actions_output_port(struct packet* pkt, uint16_t max_len, uint64_t cookie) { - OFSwitch13Device::DpActionsOutputPort(pkt, - out_port, - out_queue, - max_len, - cookie); + OFSwitch13Device::DpActionsOutputPort(pkt, out_port, out_queue, max_len, cookie); } void @@ -172,7 +165,7 @@ dpctl_transact_and_print(struct vconn* vconn, struct ofl_msg_header* req, struct ofl_msg_header** repl) { - // Different from bofus dpctl, this transaction doesn't wait for a reply, + // Different from BOFUSS dpctl, this transaction doesn't wait for a reply, // as ns-3 socket library doesn't provide blocking sockets. So, we send the // request and return. The reply will came later, using the ns-3 callback // mechanism. diff --git a/model/ofswitch13-interface.h b/model/ofswitch13-interface.h index 9cfb71b..f39ee1a 100644 --- a/model/ofswitch13-interface.h +++ b/model/ofswitch13-interface.h @@ -60,8 +60,8 @@ extern "C" #include #include #include -#include #include +#include #include #include #include @@ -81,10 +81,9 @@ typedef void (*OpenFlowCallback)(Ptr packet); /** * \ingroup ofswitch13 - * Enable the logging system of the BOFUSS library. - * By default, it will configure de logging system for maximum verbose dump on - * console. You can set the \p printToFile parameter to dump messages to file - * instead. + * Enable the logging system of the BOFUSS library. By default, it will + * configure de logging system for maximum verbose dump on console. You can set + * the \p printToFile parameter to dump messages to file instead. * \param printToFile Dump log messages to file instead of console. * \param prefix Filename prefix to use for log files. * \param explicitFilename Treat the prefix as an explicit filename if true. @@ -97,17 +96,15 @@ void EnableBofussLog(bool printToFile = false, /** * \ingroup ofswitch13 - * Create an internal BOFUSS buffer from ns3::Packet. Takes a - * Ptr and generates a buffer (struct ofpbuf*) from it, loading the - * packet data as well as its headers into the buffer. + * Create an internal BOFUSS buffer from ns3::Packet. Takes a Ptr and + * generates a buffer (struct ofpbuf*) from it, loading the packet data as well + * as its headers into the buffer. * \param packet The ns-3 packet. * \param bodyRoom The size to allocate for data. - * \param headRoom The size to allocate for headers (left unitialized). + * \param headRoom The size to allocate for headers (left uninitialized). * \return The OpenFlow Buffer created from the packet. */ -struct ofpbuf* BufferFromPacket(Ptr packet, - size_t bodyRoom, - size_t headRoom = 0); +struct ofpbuf* BufferFromPacket(Ptr packet, size_t bodyRoom, size_t headRoom = 0); /** * \ingroup ofswitch13 @@ -122,9 +119,9 @@ Ptr PacketFromMsg(struct ofl_msg_header* msg, uint32_t xid = 0); /** * \ingroup ofswitch13 - * Create a new ns3::Packet from internal BOFUSS buffer. Takes a buffer - * (struct ofpbuf*) and generates a Ptr from it, load the data as well - * as its headers into the packet. + * Create a new ns3::Packet from internal BOFUSS buffer. Takes a buffer (struct + * ofpbuf*) and generates a Ptr from it, load the data as well as its + * headers into the packet. * \param buffer The internal buffer. * \return The ns3::Packet created. */ diff --git a/model/ofswitch13-learning-controller.cc b/model/ofswitch13-learning-controller.cc index 7ab1456..6e176de 100644 --- a/model/ofswitch13-learning-controller.cc +++ b/model/ofswitch13-learning-controller.cc @@ -71,9 +71,7 @@ OFSwitch13LearningController::HandlePacketIn(struct ofl_msg_packet_in* msg, // Get the switch datapath ID uint64_t swDpId = swtch->GetDpId(); - char* msgStr = - ofl_structs_match_to_string((struct ofl_match_header*)msg->match, - nullptr); + char* msgStr = ofl_structs_match_to_string((struct ofl_match_header*)msg->match, nullptr); NS_LOG_DEBUG("Packet in match: " << msgStr); free(msgStr); @@ -129,24 +127,25 @@ OFSwitch13LearningController::HandlePacketIn(struct ofl_msg_packet_in* msg, } else { - NS_LOG_DEBUG("Learning that mac " - << src48 << " can be found at port " - << inPort); + NS_LOG_DEBUG("Learning mac " << src48 << " at port " << inPort); // Send a flow-mod to switch creating this flow. Let's // configure the flow entry to 10s idle timeout and to // notify the controller when flow expires. (flags=0x0001) + + // clang-format off std::ostringstream cmd; cmd << "flow-mod cmd=add,table=0,idle=10,flags=0x0001" - << ",prio=" << ++prio << " eth_dst=" << src48 + << ",prio=" << ++prio + << " eth_dst=" << src48 << " apply:output=" << inPort; + // clang-format on DpctlExecute(swDpId, cmd.str()); } } else { - NS_ASSERT_MSG(itSrc->second == inPort, - "Inconsistent L2 switching table"); + NS_ASSERT_MSG(itSrc->second == inPort, "Inconsistent L2 switching table"); } } else @@ -170,8 +169,8 @@ OFSwitch13LearningController::HandlePacketIn(struct ofl_msg_packet_in* msg, } // Create output action - struct ofl_action_output* a = (struct ofl_action_output*)xmalloc( - sizeof(struct ofl_action_output)); + struct ofl_action_output* a = + (struct ofl_action_output*)xmalloc(sizeof(struct ofl_action_output)); a->header.type = OFPAT_OUTPUT; a->port = outPort; a->max_len = 0; @@ -184,7 +183,7 @@ OFSwitch13LearningController::HandlePacketIn(struct ofl_msg_packet_in* msg, } else { - NS_LOG_WARN("This controller can't handle the packet. Unkwnon reason."); + NS_LOG_WARN("This controller can't handle the packet. Unknown reason."); } // All handlers must free the message when everything is ok @@ -193,10 +192,9 @@ OFSwitch13LearningController::HandlePacketIn(struct ofl_msg_packet_in* msg, } ofl_err -OFSwitch13LearningController::HandleFlowRemoved( - struct ofl_msg_flow_removed* msg, - Ptr swtch, - uint32_t xid) +OFSwitch13LearningController::HandleFlowRemoved(struct ofl_msg_flow_removed* msg, + Ptr swtch, + uint32_t xid) { NS_LOG_FUNCTION(this << swtch << xid); @@ -209,8 +207,7 @@ OFSwitch13LearningController::HandleFlowRemoved( { Mac48Address mac48; struct ofl_match_tlv* ethSrc = - oxm_match_lookup(OXM_OF_ETH_DST, - (struct ofl_match*)msg->stats->match); + oxm_match_lookup(OXM_OF_ETH_DST, (struct ofl_match*)msg->stats->match); mac48.CopyFrom(ethSrc->value); L2Table_t* l2Table = &it->second; @@ -235,12 +232,10 @@ OFSwitch13LearningController::HandshakeSuccessful(Ptr swtch) // Get the switch datapath ID uint64_t swDpId = swtch->GetDpId(); - // After a successfull handshake, let's install the table-miss entry, - // setting to 128 bytes the maximum amount of data from a packet that should - // be sent to the controller. - DpctlExecute(swDpId, - "flow-mod cmd=add,table=0,prio=0 " - "apply:output=ctrl:128"); + // After a successful handshake, let's install the table-miss entry, setting + // to 128 bytes the maximum amount of data from a packet that should be sent + // to the controller. + DpctlExecute(swDpId, "flow-mod cmd=add,table=0,prio=0 apply:output=ctrl:128"); // Configure te switch to buffer packets and send only the first 128 bytes // of each packet sent to the controller when not using an output action to diff --git a/model/ofswitch13-learning-controller.h b/model/ofswitch13-learning-controller.h index 01834d8..455f5e0 100644 --- a/model/ofswitch13-learning-controller.h +++ b/model/ofswitch13-learning-controller.h @@ -48,7 +48,6 @@ class OFSwitch13LearningController : public OFSwitch13Controller * Handle packet-in messages sent from switch to this controller. Look for * L2 switching information, update the structures and send a packet-out * back. - * * \param msg The packet-in message. * \param swtch The switch information. * \param xid Transaction id. @@ -61,7 +60,6 @@ class OFSwitch13LearningController : public OFSwitch13Controller /** * Handle flow removed messages sent from switch to this controller. Look * for L2 switching information and removes associated entry. - * * \param msg The flow removed message. * \param swtch The switch information. * \param xid Transaction id. @@ -90,7 +88,7 @@ class OFSwitch13LearningController : public OFSwitch13Controller /** Map datapathID to L2SwitchingTable */ typedef std::map DatapathMap_t; - /** Switching information for all dapataths */ + /** Switching information for every datapath */ DatapathMap_t m_learnedInfo; //\} }; diff --git a/model/ofswitch13-port.cc b/model/ofswitch13-port.cc index edbfc40..2869404 100644 --- a/model/ofswitch13-port.cc +++ b/model/ofswitch13-port.cc @@ -30,8 +30,7 @@ #include #undef NS_LOG_APPEND_CONTEXT -#define NS_LOG_APPEND_CONTEXT \ - std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; +#define NS_LOG_APPEND_CONTEXT std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; namespace ns3 { @@ -88,30 +87,26 @@ OFSwitch13Port::GetTypeId() .SetParent() .SetGroupName("OFSwitch13") .AddConstructor() - .AddAttribute( - "PortQueue", - "The OpenFlow queue to use as the TX queue in this port.", - PointerValue(), - MakePointerAccessor(&OFSwitch13Port::m_portQueue), - MakePointerChecker()) - .AddAttribute( - "QueueFactory", - "The object factory for the OpenFlow queue.", - TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, - ObjectFactoryValue(GetDefaultQueueFactory()), - MakeObjectFactoryAccessor(&OFSwitch13Port::m_factQueue), - MakeObjectFactoryChecker()) - - .AddTraceSource( - "SwitchPortRx", - "Trace source indicating a packet received at this port.", - MakeTraceSourceAccessor(&OFSwitch13Port::m_rxTrace), - "ns3::Packet::TracedCallback") - .AddTraceSource( - "SwitchPortTx", - "Trace source indicating a packet sent at this port.", - MakeTraceSourceAccessor(&OFSwitch13Port::m_txTrace), - "ns3::Packet::TracedCallback"); + .AddAttribute("PortQueue", + "The OpenFlow queue to use as the TX queue in this port.", + PointerValue(), + MakePointerAccessor(&OFSwitch13Port::m_portQueue), + MakePointerChecker()) + .AddAttribute("QueueFactory", + "The object factory for the OpenFlow queue.", + TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, + ObjectFactoryValue(GetDefaultQueueFactory()), + MakeObjectFactoryAccessor(&OFSwitch13Port::m_factQueue), + MakeObjectFactoryChecker()) + + .AddTraceSource("SwitchPortRx", + "Trace source indicating a packet received at this port.", + MakeTraceSourceAccessor(&OFSwitch13Port::m_rxTrace), + "ns3::Packet::TracedCallback") + .AddTraceSource("SwitchPortTx", + "Trace source indicating a packet sent at this port.", + MakeTraceSourceAccessor(&OFSwitch13Port::m_txTrace), + "ns3::Packet::TracedCallback"); return tid; } @@ -143,8 +138,7 @@ OFSwitch13Port::NotifyConstructionCompleted() // Check for valid NetDevice type Ptr csmaDev = m_netDev->GetObject(); Ptr virtDev = m_netDev->GetObject(); - NS_ABORT_MSG_IF(!csmaDev && !virtDev, - "NetDevice must be CsmaNetDevice or VirtualNetDevice."); + NS_ABORT_MSG_IF(!csmaDev && !virtDev, "NetDevice must be CsmaNetDevice or VirtualNetDevice."); // Filling BOFUSS internal structures for this port. size_t oflPortSize = sizeof(struct ofl_port); @@ -173,8 +167,8 @@ OFSwitch13Port::NotifyConstructionCompleted() m_swPort->flags |= SWP_USED; // To avoid a null check failure in BOFUSS - // dp_ports_handle_stats_request_port (), we are pointing m_swPort->netdev - // to corresponding ns3::NetDevice, but this pointer must not be used! + // dp_ports_handle_stats_request_port(), we are pointing m_swPort->netdev to + // corresponding ns3::NetDevice, but this pointer must not be used! m_swPort->netdev = (struct netdev*)PeekPointer(m_netDev); // Creating the OFSwitch13Queue for this switch port @@ -203,14 +197,12 @@ OFSwitch13Port::NotifyConstructionCompleted() // Register the receive callback to get packets from the NetDevice. if (csmaDev) { - csmaDev->SetOpenFlowReceiveCallback( - MakeCallback(&OFSwitch13Port::Receive, this)); + csmaDev->SetOpenFlowReceiveCallback(MakeCallback(&OFSwitch13Port::Receive, this)); } else { NS_ASSERT(virtDev); - virtDev->SetOpenFlowReceiveCallback( - MakeCallback(&OFSwitch13Port::Receive, this)); + virtDev->SetOpenFlowReceiveCallback(MakeCallback(&OFSwitch13Port::Receive, this)); } } @@ -335,7 +327,7 @@ OFSwitch13Port::Receive(Ptr device, // Check port configuration. if ((m_swPort->conf->config & (OFPPC_NO_RECV | OFPPC_PORT_DOWN)) != 0) { - NS_LOG_WARN("This port is down or inoperating. Discarding packet"); + NS_LOG_WARN("This port is down or inoperative. Discarding packet"); return false; } @@ -361,9 +353,7 @@ OFSwitch13Port::Receive(Ptr device, } bool -OFSwitch13Port::Send(Ptr packet, - uint32_t queueNo, - uint64_t tunnelId) +OFSwitch13Port::Send(Ptr packet, uint32_t queueNo, uint64_t tunnelId) { NS_LOG_FUNCTION(this << packet << queueNo << tunnelId); @@ -377,8 +367,7 @@ OFSwitch13Port::Send(Ptr packet, m_txTrace(packet); Ptr packetCopy = packet->Copy(); - NS_LOG_DEBUG("Pkt " << packetCopy->GetUid() - << " will be sent at this port."); + NS_LOG_DEBUG("Pkt " << packetCopy->GetUid() << " will be sent at this port."); // Removing the Ethernet header and trailer from packet, which will be // included again by CsmaNetDevice diff --git a/model/ofswitch13-port.h b/model/ofswitch13-port.h index c30ab4d..f3c3a66 100644 --- a/model/ofswitch13-port.h +++ b/model/ofswitch13-port.h @@ -40,12 +40,12 @@ class OFSwitch13Device; /** * \ingroup ofswitch13 * - * A OpenFlow switch port, interconnecting the underlying NetDevice to the - * OpenFlow device through the OpenFlow receive callback. This class handles - * the BOFUSS internal sw_port structure. + * A OpenFlow switch port to interconnect the underlying NetDevice to the + * OpenFlow device through the OpenFlow receive callback. This class handles the + * BOFUSS internal sw_port structure. * \see BOFUSS udatapath/dp_ports.h - * \attention Each underlying NetDevice used as port must only be assigned - * a MAC Address. Adding an Ipv4/IPv6 layer to it may cause error. + * \attention Each underlying NetDevice used as port must only be assigned a MAC + * Address. Adding an Ipv4/IPv6 layer to it may cause error. */ class OFSwitch13Port : public Object { @@ -56,14 +56,12 @@ class OFSwitch13Port : public Object /** * Complete Constructor. Create and populate a new datapath port, notifying * the controller of this new port. - * \see ofsoftswitch new_port () at udatapath/dp_ports.c + * \see BOFUSS new_port() at udatapath/dp_ports.c * \param dp The datapath. * \param netDev The underlying NetDevice. * \param openflowDev The OpenFlow device. */ - OFSwitch13Port(struct datapath* dp, - Ptr netDev, - Ptr openflowDev); + OFSwitch13Port(struct datapath* dp, Ptr netDev, Ptr openflowDev); /** * Register this type. @@ -112,15 +110,13 @@ class OFSwitch13Port : public Object * Send a packet over this OpenFlow switch port. It will check port * configuration, update counters and send the packet to the underlying * device. - * \see BOFUSS function dp_ports_run () at udatapath/dp_ports.c + * \see BOFUSS function dp_ports_run() at udatapath/dp_ports.c * \param packet The Packet to send. * \param queueNo The queue to use. * \param tunnelId The metadata associated with a logical port. * \return true if the packet was sent successfully, false otherwise. */ - bool Send(Ptr packet, - uint32_t queueNo = 0, - uint64_t tunnelId = 0); + bool Send(Ptr packet, uint32_t queueNo = 0, uint64_t tunnelId = 0); protected: /** Destructor implementation */ @@ -132,7 +128,7 @@ class OFSwitch13Port : public Object private: /** * Create the bitmaps of OFPPF_* describing port features. - * \see ofsoftswitch netdev_get_features () at lib/netdev.c + * \see BOFUSS netdev_get_features() at lib/netdev.c * \return Port features bitmap. */ uint32_t GetPortFeatures(); @@ -141,11 +137,11 @@ class OFSwitch13Port : public Object * Called when a packet is received on this OpenFlow switch port by the * underlying NetDevice. It will check port configuration, update counter * and send the packet to the OpenFlow pipeline. - * \see BOFUSS function dp_ports_run () at udatapath/dp_ports.c + * \see BOFUSS function dp_ports_run() at udatapath/dp_ports.c * \param device Underlying ns-3 network device. * \param packet The received packet. * \param protocol Next protocol header value. - * \param from Address of the correspondant. + * \param from Address of the correspondent. * \param to Address of the destination. * \param packetType Type of the packet. * \return true. diff --git a/model/ofswitch13-priority-queue.cc b/model/ofswitch13-priority-queue.cc index 473dba4..961cd5c 100644 --- a/model/ofswitch13-priority-queue.cc +++ b/model/ofswitch13-priority-queue.cc @@ -23,8 +23,7 @@ #include "ns3/string.h" #undef NS_LOG_APPEND_CONTEXT -#define NS_LOG_APPEND_CONTEXT \ - std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; +#define NS_LOG_APPEND_CONTEXT std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; namespace ns3 { @@ -50,19 +49,17 @@ OFSwitch13PriorityQueue::GetTypeId() .SetParent() .SetGroupName("OFSwitch13") .AddConstructor() - .AddAttribute( - "NumQueues", - "The number of internal priority queues.", - TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, - UintegerValue(1), - MakeUintegerAccessor(&OFSwitch13PriorityQueue::m_numQueues), - MakeUintegerChecker(1, PORT_MAX_QUEUES)) + .AddAttribute("NumQueues", + "The number of internal priority queues.", + TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, + UintegerValue(1), + MakeUintegerAccessor(&OFSwitch13PriorityQueue::m_numQueues), + MakeUintegerChecker(1, PORT_MAX_QUEUES)) .AddAttribute("QueueFactory", "The object factory for internal priority queues.", TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, ObjectFactoryValue(GetDefaultQueueFactory()), - MakeObjectFactoryAccessor( - &OFSwitch13PriorityQueue::m_facQueues), + MakeObjectFactoryAccessor(&OFSwitch13PriorityQueue::m_facQueues), MakeObjectFactoryChecker()); return tid; } diff --git a/model/ofswitch13-priority-queue.h b/model/ofswitch13-priority-queue.h index bc806e6..62c8ff1 100644 --- a/model/ofswitch13-priority-queue.h +++ b/model/ofswitch13-priority-queue.h @@ -31,9 +31,9 @@ extern template class Queue; /** * \ingroup ofswitch13 * - * This class implements the priority queuing discipline for OpenFlow queue. - * It creates a collection of N priority queues, identified by IDs ranging from - * 0 to N-1 with decreasing priority (queue ID 0 has the highest priority). The + * This class implements the priority queuing discipline for OpenFlow queue. It + * creates a collection of N priority queues, identified by IDs ranging from 0 + * to N-1 with decreasing priority (queue ID 0 has the highest priority). The * output scheduling algorithm ensures that higher-priority queues are always * served first. */ diff --git a/model/ofswitch13-queue.cc b/model/ofswitch13-queue.cc index e3b8f15..b34ea20 100644 --- a/model/ofswitch13-queue.cc +++ b/model/ofswitch13-queue.cc @@ -26,8 +26,7 @@ #include "ns3/string.h" #undef NS_LOG_APPEND_CONTEXT -#define NS_LOG_APPEND_CONTEXT \ - std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; +#define NS_LOG_APPEND_CONTEXT std::clog << "[dp " << m_dpId << " port " << m_portNo << "] "; namespace ns3 { @@ -38,15 +37,14 @@ NS_OBJECT_ENSURE_REGISTERED(OFSwitch13Queue); TypeId OFSwitch13Queue::GetTypeId() { - static TypeId tid = - TypeId("ns3::OFSwitch13Queue") - .SetParent>() - .SetGroupName("OFSwitch13") - .AddAttribute("QueueList", - "The list of internal queues.", - ObjectVectorValue(), - MakeObjectVectorAccessor(&OFSwitch13Queue::m_queues), - MakeObjectVectorChecker>()); + static TypeId tid = TypeId("ns3::OFSwitch13Queue") + .SetParent>() + .SetGroupName("OFSwitch13") + .AddAttribute("QueueList", + "The list of internal queues.", + ObjectVectorValue(), + MakeObjectVectorAccessor(&OFSwitch13Queue::m_queues), + MakeObjectVectorChecker>()); return tid; } @@ -86,9 +84,9 @@ OFSwitch13Queue::Enqueue(Ptr packet) swQueue->stats->tx_packets++; swQueue->stats->tx_bytes += packet->GetSize(); - // Enqueue the packet in this queue too. - // This is necessary to ensure consistent statistics. Otherwise, when - // the NetDevice calls the IsEmpty () method, it will return true. + // Enqueue the packet in this queue too. This is necessary to ensure + // consistent statistics. Otherwise, when the NetDevice calls the + // IsEmpty() method, it will return true. DoEnqueue(GetContainer().end(), packet); } else @@ -96,8 +94,8 @@ OFSwitch13Queue::Enqueue(Ptr packet) NS_LOG_DEBUG("Packet enqueue dropped by internal queue " << queueId); swQueue->stats->tx_errors++; - // Drop the packet in this queue too. - // This is necessary to ensure consistent statistics. + // Drop the packet in this queue too. This is necessary to ensure + // consistent statistics. DropBeforeEnqueue(packet); } return retval; @@ -130,8 +128,8 @@ OFSwitch13Queue::DoDispose() { NS_LOG_FUNCTION(this); - // While m_swPort is valid, free internal stats and props - // structures for each available queue + // While m_swPort is valid, free internal stats and props structures for + // each available queue if (m_swPort) { struct sw_queue* swQueue; @@ -197,8 +195,8 @@ OFSwitch13Queue::NotifyDequeue(Ptr packet) { NS_LOG_FUNCTION(this << packet); - // Dequeue the packet from this queue too. As we don't know the - // exactly packet location on this queue, we have to look for it. + // Dequeue the packet from this queue too. As we don't know the exactly + // packet location on this queue, we have to look for it. for (auto it = GetContainer().begin(); it != GetContainer().end(); it++) { if ((*it) == packet) @@ -215,8 +213,8 @@ OFSwitch13Queue::NotifyRemove(Ptr packet) { NS_LOG_FUNCTION(this << packet); - // Remove the packet from this queue too. As we don't know the - // exactly packet location on this queue, we have to look for it. + // Remove the packet from this queue too. As we don't know the exactly + // packet location on this queue, we have to look for it. for (auto it = GetContainer().begin(); it != GetContainer().end(); it++) { if ((*it) == packet) diff --git a/model/ofswitch13-queue.h b/model/ofswitch13-queue.h index af422b9..d6a290d 100644 --- a/model/ofswitch13-queue.h +++ b/model/ofswitch13-queue.h @@ -38,18 +38,18 @@ extern template class Queue; * An OpenFlow switch provides limited Quality-of-Service support (QoS) through * a simple queuing mechanism. One (or more) queues can attach to a port and be * used to map flow entries on it. Flow entries mapped to a specific queue will - * be treated according to that queue's configuration. Queue configuration - * takes place outside the OpenFlow protocol. + * be treated according to that queue's configuration. Queue configuration takes + * place outside the OpenFlow protocol. * * This class implements the queue interface, extending the ns3::Queue * class to allow compatibility with the CsmaNetDevice used by OFSwitch13Port. * Internally, it holds a collection of N (possibly different) queues, - * identified by IDs ranging from 0 to N-1. The Enqueue () method uses the + * identified by IDs ranging from 0 to N-1. The Enqueue() method uses the * ns3::QueueTag to identify which internal queue will hold the packet. - * Subclasses can perform different output scheduling algorithms by - * implementing the Dequeue (), Remove () and Peek () methods, always calling - * the NotifyDequeue () and NotifyRemoved () methods from this base class to - * keep consistency. + * Subclasses can perform different output scheduling algorithms by implementing + * the Dequeue(), Remove() and Peek() methods, always calling the + * NotifyDequeue() and NotifyRemoved() methods from this base class to keep + * consistency. */ class OFSwitch13Queue : public Queue { @@ -77,7 +77,7 @@ class OFSwitch13Queue : public Queue * \param queueId The queue id. * \return The queue pointer. * \internal This function is marked as const to allow its usage inside - * DoPeek () member function. + * DoPeek() member function. */ Ptr> GetQueue(int queueId) const; @@ -118,8 +118,7 @@ class OFSwitch13Queue : public Queue uint32_t m_portNo; //!< OpenFlow port number. private: - /** Structure to save the list of internal queues in this queue interface. - */ + /** Structure to save the list of internal queues in this interface. */ typedef std::vector> QueueList_t; struct sw_port* m_swPort; //!< BOFUSS port structure. diff --git a/model/ofswitch13-socket-handler.cc b/model/ofswitch13-socket-handler.cc index a6c0dd3..ea1d1d8 100644 --- a/model/ofswitch13-socket-handler.cc +++ b/model/ofswitch13-socket-handler.cc @@ -28,10 +28,12 @@ NS_OBJECT_ENSURE_REGISTERED(OFSwitch13SocketHandler); TypeId OFSwitch13SocketHandler::GetTypeId() { + // clang-format off static TypeId tid = TypeId("ns3::OFSwitch13SocketHandler") .SetParent() .SetGroupName("OFSwitch13"); return tid; + // clang-format on } OFSwitch13SocketHandler::OFSwitch13SocketHandler(Ptr socket) diff --git a/model/ofswitch13-socket-handler.h b/model/ofswitch13-socket-handler.h index e7b1a78..97d124e 100644 --- a/model/ofswitch13-socket-handler.h +++ b/model/ofswitch13-socket-handler.h @@ -34,15 +34,15 @@ namespace ns3 /** * \ingroup ofswitch13 - * Class used to read/send single OpenFlow message from/to an open socket. - * The TCP socket receive callback is connected to the Recv () method, which is + * Class used to read/send single OpenFlow message from/to an open socket. The + * TCP socket receive callback is connected to the Recv() method, which is * responsible for reading the correct number of bytes of a complete OpenFlow * message. When the OpenFlow message is completely received, it is sent to the - * connected callback that was previously set using the SetReceiveCallback () + * connected callback that was previously set using the SetReceiveCallback() * method. On the other direction, the TCP socket send callback is connected to - * the Send () method that forwards OpenFlow message received by the - * SendMessage () method to the open socket, respecting the original order of - * the messages. + * the Send() method that forwards OpenFlow message received by the + * SendMessage() method to the open socket, respecting the original order of the + * messages. */ class OFSwitch13SocketHandler : public Object { From 3fbc7b2e9346318155369d9d739887e47d8de5f2 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Thu, 20 Apr 2023 00:00:21 -0300 Subject: [PATCH 03/10] Updating the maximum size in OFSwitch13Queue. --- RELEASE_NOTES | 10 ++++++++++ model/ofswitch13-queue.cc | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index d835fe9..65d69fa 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -5,6 +5,16 @@ OFSwitch13 release notes This file contains OFSwitch13 release notes (most recent releases first). +Release X.X.X (XXX XX, XXXX) +============================ + +- Updating buffer timeout operation with millisecond resolution. + +- Updating the maximum size of the parent OFSwitch13Queue class with the sum of + maximum size of internal queues. The operation mode (packets or bytes) of all + internal queues must be the same. + + Release 5.2.1 (Mar 31, 2023) ============================ diff --git a/model/ofswitch13-queue.cc b/model/ofswitch13-queue.cc index b34ea20..4cd1f39 100644 --- a/model/ofswitch13-queue.cc +++ b/model/ofswitch13-queue.cc @@ -187,6 +187,17 @@ OFSwitch13Queue::AddQueue(Ptr> queue) m_queues.emplace_back(queue); NS_LOG_DEBUG("New queue with ID " << queueId); + // Update the size of this queue. + uint32_t maxSizeValue = 0; + QueueSizeUnit maxSizeUnit = m_queues.at(0)->GetMaxSize().GetUnit(); + for (const auto& queueIt : m_queues) + { + NS_ASSERT_MSG(queueIt->GetMaxSize().GetUnit() == maxSizeUnit, + "Be consistent with queues operation modes."); + maxSizeValue += queueIt->GetMaxSize().GetValue(); + } + SetMaxSize(QueueSize(maxSizeUnit, maxSizeValue)); + return queueId; } From 43ae19977817d7ecd113ae9603ffb713d5bed3c0 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Thu, 31 Aug 2023 17:59:52 -0300 Subject: [PATCH 04/10] Fix set-field action in packet out messages. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11d326a..7601fb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ if(NOT bofuss_FOUND AND NOT ${bofuss_FOUND}) ExternalProject_Add( bofuss_dep GIT_REPOSITORY https://github.com/ljerezchaves/ofsoftswitch13.git - GIT_TAG 625f01384522579db8f1c361209319530d53774a + GIT_TAG 1b6106e3e3814c50b323e43b6bb2dec5ab13ffe7 PREFIX bofuss_dep BUILD_IN_SOURCE TRUE UPDATE_DISCONNECTED TRUE From 7c8df51fad224328737e3423891758fe875f5105 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 08:49:58 -0300 Subject: [PATCH 05/10] Removing example dependency from netanim. --- CMakeLists.txt | 11 +-- RELEASE_NOTES | 2 + examples/CMakeLists.txt | 2 +- .../images/client.png | Bin 49994 -> 0 bytes .../images/controller.png | Bin 24276 -> 0 bytes .../images/server.png | Bin 39872 -> 0 bytes .../images/switch.png | Bin 112127 -> 0 bytes examples/ofswitch13-qos-controller/main.cc | 70 ------------------ 8 files changed, 9 insertions(+), 76 deletions(-) delete mode 100644 examples/ofswitch13-qos-controller/images/client.png delete mode 100644 examples/ofswitch13-qos-controller/images/controller.png delete mode 100644 examples/ofswitch13-qos-controller/images/server.png delete mode 100644 examples/ofswitch13-qos-controller/images/switch.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 7601fb5..0592252 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ endif() # Check for BOFUSS dependency -# List of BOFUSS headers files required by OFSwtich13 +# List of BOFUSS headers files required by OFSwitch13 set(NS3_OFSWITCH13_BOFUSS_HEADERS bofuss/action_set.h bofuss/datapath.h @@ -143,11 +143,12 @@ set(header_files set(test_sources ) -# Library to link the OFSwitch13 module +# Libraries to link to the OFSwitch13 module set(libraries_to_link - ${libcsma} + ${libcore} + ${libnetwork} ${libinternet} - ${libapplications} + ${libcsma} ${libpoint-to-point} ${libvirtual-net-device} ${bofuss_LIBRARIES} @@ -162,7 +163,7 @@ build_lib( TEST_SOURCES ${test_sources} ) -# Add BOFUSS dependecy to OFswitch13 module +# Add BOFUSS dependecy to OFSwitch13 module if(NOT bofuss_FOUND AND NOT ${bofuss_FOUND}) add_dependencies(${libofswitch13} bofuss_dep) if(NOT ${XCODE}) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 65d69fa..badbfdb 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -8,6 +8,8 @@ This file contains OFSwitch13 release notes (most recent releases first). Release X.X.X (XXX XX, XXXX) ============================ +- Removing examples/ofswitch13-qos-controller dependency from netanim module. + - Updating buffer timeout operation with millisecond resolution. - Updating the maximum size of the parent OFSwitch13Queue class with the sum of diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 9d1edea..06c70c7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -53,7 +53,7 @@ build_lib_example( ofswitch13-qos-controller/qos-controller.cc LIBRARIES_TO_LINK ${libofswitch13} - ${libnetanim} + ${libapplications} ) build_lib_example( diff --git a/examples/ofswitch13-qos-controller/images/client.png b/examples/ofswitch13-qos-controller/images/client.png deleted file mode 100644 index d4d1fda54eb778606fc74b3034e78ca5a0e0a4aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49994 zcmeFY^;=Zm7dAXWr;30`8=!QjzyK;L5{lA8mvjtWL#rsDC?Gk2(%lT*4Wo1n9Rm#A z{Tx2u_x%%|-<|<4F3yGL?6dZY`(F3jpVU>AD9P@Ufj}V2moJ{a1%bf(AP|I*gb29u zV4FG@_(6<#sq&0?ocJaUH_u5{7!`1d$V6G`Dd_6@3ssjH4FWaeUp|$8=P`j#A|BIk zbG>99eW02+=A=I#YN~%Lv!hE+m5EbKOstCji%N9V)0+_X8!t#m*#jP}v5}?*J|N&i z1$u4$GX6^N*jSHpZeX!=Lq_S=u|4z@8 zGs&@;ly#0fA}A>6zVncMN!VyvmfOOGs|rK-`|m7yA8=iqDd%qWWRemR5)jkYk2eeR z&0o#rVb!yV2DBaxRrw{T*FW+AqZ|y;Zwc zV%!p1tZi^t#7cdn(6Aw|V)>EL)yzbSbYVF1ZA`yze^xrXKgPxf9{ILy?aH>tyIJmQ$1codPY$>9ex z2fRkVoi*XK+>Iy>5$d3Rq)tla|vrtNX@D~BwuLvI`wbAJT|rAxP*M@>-fN*)k>DmZwk93wKN?of9yg7?2V z^8Y2l^rtnqus&YqWHEXi?Ye$z=EM0dY(@+6 z3*X@1YmGV$B+JBZ)Aj=kN-Ejw8D?b9Kzw3+ct~=!>E2)(==hGW=>1#2?be{Ik{Ewq zd~rv8NTD+oRfPS`@`P#lu8LzL5=<%n5>L83n`M0@T~YR_T*~(p0gt()t?vemA|)Fk zxg1xSc3-<5oMxjv#paG5YZ{u(LYyCW$Q2Z7;nn1perUYQid^7My0*XBilMsjUA&}W zotJ9?>*aRXYl?_&%T>Kf`~1T3a6a_M8tk>H4fofE*KH}5Zzxc{ox1bav9;VK!tEn5 zt4~qA{a+@|!qxcXmH*uk9`m2} zmKbNIs$JH_+d5eN6JIv{6$^+SZ=GoSV7rT6Hhq7+N@ff_pMG3Q8qpu?kBV>>y4CxU zPrhUQuP*ZJ*@mk8_tRM)%3z4UI+<)xvC1X}T(6=oAcD%>Nu!lgVtFKOsV85$KV8!SVKRzyVG!^Zh)j`(lPH&@?Crl(oOiT=wxrOn++)J?(w(NhZ@m3@KR=8^u zA-&nz;f5&+Sg+&luXn9hpcM*&0rlQToA|OpRYk>Hxj_#LQ`C){8ShS-fPH~1L|Hfh zTbZ3TLJ_xe7uJ3sop(CV$gL=Iu zzh`Td+-p=Tr<|4DmVa4_<;8~c*GpV^*B1jJa5`arZ5{y(Ip~5|?pWSHcx$rK0p1z& znvEDGfmWKW2{lDQn8&3eU?G(Jg^YQM|Cw9q0qTq;LF&W5-%L1#J8#oV^S(7XAqE=V z`C)wPn>AbgnKhrvgYuZAAKbw)0iRYJkfVI6O?8N*LK}|FRxVBta>*w{)m`2j_v?6i z6aFEX=gtGeE@B(b&4cLp4W^1heM%7wIIE)Q(F34V^j_}@&JSi-X%FX1S$>fC>??&b zZbZ+)&iXj#twl2A9gkerec{+^&-r3YYeV8af3C;JH3 z?f+9Qy}USEQP7}S`y5DPRjBmGhRR&TRI>#Yn11>MN{2PpUjWxkgAYy~kAD|XEzMM=cj5+YNO#Ms>f1Yi4K2JYR z&R?q@zkOCbPoBbyM-)dlahx;wY$+6Bm&!%_FMa-JNgY_j#o^?hosxq4w5Mf@z%9>| zYOZtlsn);94r~95fva;h__3wgY=O*rR!R@G?n;uofiJI0lGM-ZU}=&kL_hJ{%Sf>A zwp0B`wlCv-Xa*&w%3DtjW64pYZMm8-x{OXa|NXEz=82~M+*6tN$43>Z3O6omC=iIO z@bcgf&O?n-w{7TQv7@5@y-a}W$2#w$<@?ehj)&!U_8zaTNP+XLyaKOtdTp)&Hbwto z_GF%Rp8_+J4DGRM@z0x(Av;({P?GI9wB3Z)cvJ3wHhGFl@!qU0rv;E2cyy23*_+wvm6d zTKY}@TLU>xLeQo&irsY_$!8SEcz&I7$dx__!E!UxlqxqTEBRCYQl&-|T)HEf^oG@4 zVqM40V~0oo0kyE}zdMkCPp{DhC8OBlF8K6hCB={)$ru0Iad4!Gk{L6$No1VBs3>|d zjx$dw_v>JGU%ZcG7m@kwANiKz6(3h;2w%hb?y2rBAzB;QsOwroU(Sd%)li9D_P2?=(ZfiXyV=db~eOio}_`&UZT2$0fRX#lEu0NHIzwtoRaV=^+S_ zk*@Xl|2zi931KGwK$7al_1yCw<6yvEc4l%nP@iMSGtC6w4ZgsOS%r+`=|sa_`b0h3 z>&$!TI6K=M;{l*BH2UwyV~zhsv^n^qV$!}Rl$zCmUI>U(sT=o9Y2=8-&r}1g-Xrhc zE0$(|iu={*&WA^UbP%}?6J?*C*xBR9iVw;W!|%2Jm&p{65rYL{_ET}>;uc&Or_1Ua zLI}YGJ(2e8=4msgYBJ+@mhK_eH<1+zYba))4v%eU<9mVhY?aqOlW5mm*VlPGufu3; zx-9p(m)Sp-hvz%|mvV_t0}|2(c-+6N--ck??x?*{(h4cC?EOlA>%Ia*4gwV;bD?TB zGp1`1-@uRhf+gS0{($ok2kX;@c73@cc#r=7_uupc26&r7(G(#D45oQn{LH>7kg$5& z@Ij{cn}8Q42&|1U+yfbl>K)Sm~0Y+vwA^ z+{x!fF&ZQ)=AZ|mNl%s$=Xql z3&vwHb`)o{4AOb}8g_@%-dlZF5y#bn!QWCh?t$_?Mj1nmaAd?q1F~m${QjWYfV}2{ z67ap++{Be2?m#6qLN~+O-k{)kGb_M%3zrebSH1Wh|3gDHJ68mCg!{XoSWcd+wV@cN zHy9*(Q9)}63U4#{GlT>~osUs&5l)3^?R>!r)jkSZ+#yg?leKS3ETp%n#F|!J9c2WL zSX}<@jHh-{e6@7Px@yUOD16oI&F6ghB($sX4YDY*ZWmT4BkEgL(Wo@CPhyDUq05!d z0pN8t_KTfEb8=*Anu=>S;}$~2cFmDCCB9U-UPtI*xa#Af1Hmsuf#!xUQIU59=gZ5V zoo~e3FNgmEY(+(;mq5p>`@{WxWmu(rLJ^@^1z6bQ>Dz1fpFND=Fe}cwrNms4!#kuhsUm?i--#LJp75H47&ObYXh{hC`S{db0-gBx=U4ALO99{xo90vz^9_(}drSA(ukqyq zD%9hs!<)#64^Ei#lRZ7+{fYT?!@(^?_^pIf!KdITy0_p6JJKo-WB$2!sal45j6ETOb`!YFH$% zmRQlk9nF2RUqWs}OFO!W&njiZ`M4%q1S|4C$(+UsX-Q$I5k-cndck-8a2~?ff}cfX zy6V59aHOIN^~`M4AN@$AelNPCZh!zFX_YIAZxKftBICyXVwQi01Ir52&2O=sFM{PI zbSvMXMlWX>t;si@W9M7K(=>lWB}<%m1g0JV7_mE~wh%#0$nlGX&d1!lYWv%!d@IMG z;^&RIXHS02rYgw39UOWe(aZel%VQg*BxafCXj|rV<+h7bY0>cakHb~pe{a5HY=%>a zyQh5@Q~WT$qLB_~y|lO7HyFs|aD$wXUKRk|a*l|cZAVZibi0Drq-qX|N4LL3@U$!q z$upOGRaEOiahUp>T5_arVkrE*VB)9ND6^n1EX`xuhNvq8$|+%lcc)f?{;wcC+Pg~I zPkR6~GrOr8xPTN2kWkj6MgKEyTX*ZlkC$?8xsCRbhh6lGxgUTwCih93T;&xLUY2*sP*CS096`=1D}Zz-_VA1F-f%@R{lIZtJ2&jj3-Idw%j;;>pyBzG)LoPC#JUTvWKD*p92W*cYmn zaSbAEN92nZ>a!T{1Wib&T4|(4`2A*A3>A@Q=5%(VgUh@)vn_r4;hC)J;puwe0Gv5x zou32;5TkQx>u?;Wbstj)NsfThS7OTh*Ee&~6dw9p7Pje${UJUj_dY9q=fiAcfQq`3 zP@4|olxM=71!At*9KkFd-(TW?JQr41C=c>oAIY!ogcb58pRAcQdq^?&RtEt9G!@I- zsQ<2W#C34m(Nrrnf>Dg=%?XQ7JA>uo!M>^;oeeSUj7p>#Q#PEdWhrdYYlSI**8~=* zIG{%9`sv@rr6;Hkb%lUj5FX9|73gJ{5=pHnuvjgc0lT`AjLx@(CzK#W-WK;^rC=Jt z?Mk*Ur;3af{l4GrepmvuUDT$*83ii*DAqBD%Q!D2C|=YoF5RMEOq!UuB{5)w9}oG* zEdDtjP3HPQ!+Mnl`eA!(^5vK?mN__DvEvIvH1(Gzc9|S?)g6X{i~>F2J`#{}V{j_B zTBu~O*A2kUf)Y}hsa$DsIPEYRE+})Rjd7%43g?U;#4L=GF~+@=U%QBH<{z_H-)Gk# zVh9-VsAEZPvyg)lbZixRGXXKf8_gXtBpiJ1*f{~2ZOyV8uwt2nQ1vbELSMU^2_ z8}W4xl&Kj{u5WOYJdXbn1fjht0irB{)Wsxish7GiEZ=Bn{GUa~+Kl;vZ)Ny=zS~~e zO!+evux{tMPkXcGh5FAQXUjd-Y)^xjne3QqgM63Aemeep$tg-gZ2=I1%p+Is5i(Mo zjMx6Ly4|;0F8(`-$s8?HqgU57HO^frE{sY2J{lY1*zhxeiX>}tOu%pQNIhOVY&e+7 zo<;*bc&=bBMOc&-1uE1n=aSy*72exYB>@l$Po_cTZu~uPU4_K+oHidpFck#j^n@L- zcT0RK^HHm$Ak=_Js>Eo@y{(rT<_jP7h;cs}y#ai&zTNwsQ;w_`h zAZpCctPW|CZeTy(Yt)0RWJe(P##PSW$q18ALv;1bk_XZ<V_fV;5yr$r;NAb?G1RllF)EzGRiwU4=QQh0`dy?f z(8Z~j;Brg4=c4pUmlLTSx}7NLk_Np{@C=wemX(ywtQfy0&%L=Mdt8Bfqj!keqoV^N z-lPd(3Trmeud!cS-_+T>^NHoc82aqA)h0(!d01gk@}3r3=+I8s)^P49a3>=(<`QQx zV#pOp+jo{J@E)@j!%5GWnfu<&;}IIn%n@7$u`AnJYR9r*$M+JaWk5U6HK4R$T{(}E z40F^)PC#P^La--~18h*hGOhzG_6(=<{m5rQmWRVN?eQzSFD)s?NKFkOnw@NoX`209 zZOv(Pi%kd_a1vUCO2rDgKo)4-+nsOxwE|g|IQA9&;_lhQzO+4*vf`A6BCwviekeVWk;j`h5g3MF3Ht?kb{i2?`X6xmg20>)|p>4%LTjq&ySdm@e z^#Z1`M$>CDQ@d4~+GV>#s z3ck{OD~x%Apob~B5M8@*?V$bad)DT_H$@Pf8qjcsV#27HElY^ms#=J!UFlB(a(V~iUKXu4CbLg3MWxzJG);cju0bg0`ht96dw|jT87qV%=7pZ4SBk&TU~%|2ocHou)>_Xa4*-{O=!fb2uq) zmQ8TpOZeS!RLFQ|7aaX!_(u@hqni?r&e2InWs8fg-omX}z`YLAg5pQAdNOi#r~WX6 zm-dL>rR=+{`|r2lXs7PPA07Bzn}7JAyW(dYx$Ogw`6kzUwjRbVUwod)ygch_C|wp_ z{TDFMyInmZcclMy=F@3X*No-hk*^UmZlohCcYpn5o~Y+Vwv9&KxX;C_b)7sXPpsh> zs(t;LzT#T>!-DG2^=)LRN|h|5Sp6GC^RMgwikwExqdC+**r0Ne1Q@Uj7e<;e_&pul z#;p=3A9tbJ6yL*fjwZeiM+U3zACI5s!U<24+^2sHFe8in>o$S_H4JDqB1=LJ+ z_s#~$H@hxvTI=HvEw5yp{SSYIZ}uIGhi~;zF%JJ3fCE=flv9aITKL!h(ewEWoHgT! zOZi6-t#M%`_uFNEFiReujelFHPw^$ad*yhz6kjj2^G)Bd4&o(pHt{pk!Z3_OCdSuv z*K}Mw^GWi$o|e3t*-Y}ppY#Ghp|gGwTs9uJD%$74b+-A1)odo|Bna;<%IwldU21A^ zAt3EGGB{pbn$jPU0KIr5eL2Bu&N(z>x*1u@GI}Ag?uixF8CXy0QVm|dc(5+opj=0P zF0x*F<1_{Tn-PuJ{W@U5$?qj5O{jZbihpO-HBeHo?3bKWvA?3zkdWMS%InQ1&8&Or z<3?0Af0B3gnZD$b+px1gSo7i@BPjp!ncGM_y-QLZ-Kn{lBRsZW=jttd#Xjr9!j(l3 zN(FwmcJ-=cPmFaainqZUbE5bDLN~o#F1`MtK4F9VDz(z5;#NSxJ2z6;(RN0F|`1Eg!f!A{+TRFhW|y6Ln@s@ba<-F(_CMPoy? zPQ9+4*X1xY@@VAo2=@r@2>*!S$X+ZbBHtm;CC@$2JI`OA*f0-HUB&0=Xfueq7N_~;?mE?Ky`Q3}5qMFhxWaxC##Tlli#9!{J03R)SF&QOY z>My<3kH4aw5KM9msiQH{p*2>k=1ENBU@Hi! zRP8JXqh?KB&&e$)u~>oNjuR)Sa0?s;Kjn%^lDsaz;6<-IP2XK~X=x4H-#S_pr)0oE z*0?Y=r~=|sdR|z^*8vg)>6#SET^>%2n92gX(qNp@3l*^-1n&?fjQHcLfYbIMb@u?Q z)~*d{)TnX2AbT$}Y_y4KCY%Z<)Vp(G9pDFA)@GGL^$3WAnb+SEBX*G11gCcxFCg3Q9UOSnrx51u={f1yk=dT5@2Tvb)>*c5TVGk5|Gn`uFSv{I59$0 zc@zSV{=*nx-}TQtOD{k*7){{@c7<0I1Y(J|(G8;@=ZD<yw{Q!Eaq)UL6nw+_ zzqmQoFZFPjs*NOXJEb>Lwh$l~0L!nakAmT5t3;^LQUvdRud*M?g9Ytp@L-K=a@8{- zSWe}mKr|H3QC0Kc>VhMRTc?`7SGl*J5h*KPjO9ZX6i+fD3&u7mVOn@MT3ilrY1*t( z85X+%{B|XkMUK02-rN{Ji=3H?fyMGVGXU8hfazlSJ_FA={WzhbA)pV@AO~&SGtlK= z%H-oMo7sQ%-^@#2N2=zh ztCfuYeO%#9{Y z8Na>~t4(40!t(LKor)UdDUXSZK7$ULa&02s%ca7epoYNx{-W!41x%l?y5Hm|#Pe6V z3oULFKhA&V6RN>N5;$)>PAIA6Qd}D9p|MIVxX(DAj^2-;AFlz}Ss@)^1o69s)i_ z{YShtn?Y*7Si{}2@S^b9%K#Q{P&lBuREMu7MUm~7+l*Ea6tD|ayi9z`v7A(cOp*$Z zc6-bDnp@I1jQUY7?|!>0Rg_Zv#AD@s$I*9fbM9Fd<; zvN~=rldu95*bzL_ts3H4)E3a1p+78fCr34GHqe`2Nz7^rV;*KruHmBcBQ~e%pO<4_ zDq3*G{n0}e*!)PhSeGiQnN@{Wady&%V;Pre`}+wo@NvoNnz~sx(M;^7j+|D&Wuztc z`$bo*Rs|DoNS;4u$sT$dFKW|i#LIQkDuDISC>O3xD#fMU3R}k!95DbrNd{bo{4*(( z>H6d^%VeEV9m}NmGAKhqPcJcG<^rvNyo}|v@8LgY!rdCObKU35+Tu66wpa~nvjltJ zay1``{&!*DJpwm!2=*3U#9E)%phq1_MpbmG)u8%a0`8(M-%4H91Q^n=n-c{ig!{ft zX9IQz0K2~un2o_ZsLn*!T?#Af5+PE$>>#+#{%{DK*l04(X)-2%^T)F@(1g|tP2*pR zK#N~ccVim1HJGW!@t;O|J>#LjL9$=zXXonoZ5_#1vaj~jtR_0N*JI-)b;_0j0ghe0 zQg>UdNP5;j^gR55$2wa<;M9+w;&v+G1bZb?ljNc;Z$i7CmP|fA8^{Khgu@Vf+pAFR z-zn__n>6Z!;1p+pAOt{LtcP0SlV=}!%dMaGQ~ zRaV&qpPjkYvy}e&{L96N@XKhvNna0w$)tZ9n^-2Td>$<08XdWTP0M;CUlW$lBX)80 zo8b(pb9fsIWY4X$zpKyjTg_zBmd|x~4CtG?3#CNIt zC-WeW=0n(-!h}*MMo^zfx5_bf?=?tJB1;d_WTQ1fRF#PD4qRkCn&@xl9OUTRIabB2w|=YuKLDnBBn7yC=Dvb9YU$eseEWzWNQQ=2yQi5VYh?y z13EqCCAyURFSYu5Iaf%MoaUG&K)<5h7U{+K97UBQf#449Jr?hHFRpwPg6<2XhMawV zj!XU!O*EEsTEk`!t!C%Wv{y-QthFoIb1K6>@p6}LvcC#$v!@3@k)SORrSp#f;o<;Q z8SHBivw&7W#C`E4Z!sQszz)P#hE04aPduIBpjyi1M@t{y)Nx^mDekDp(&3J#T+|Ma z7a~`z=}`?!%G`V5;{*uS$)t&ESD-Hn$&s_E^=UQfM#R*+ zfPNi>4ZGdLmq40v|i#$KE^m(05Tx=3?FLXZrEG}vsLQiC+yv1dKK4~Q(-`9 z`;u>MP&)ZItQZIz7gWmh&p%g9bK=hkz4G=te34W=Kyq;g*#(NcublDoZuyXwm7-yB z6|QsPh;gVtS=MJJMZ6Af3CV+CC*9P>nPO2fEZoY}Azp*BuHl4ggf*m_D#*%4gOCdH zS&oky@_d#{===}QjHd0!tJlp$Q%VQ2Tg(*jZe{?M-jcB}y@4?em_#I3@ndQ<=->}w z8vBdUYRk>Gv4GN3VO%CzuSSpALA@Y41%@CJyR5}g{#m0H=L#{YK)nQV>v_V0Pyfrp z?*}YIuDTWWP-%uuqE7FZp1id4s!<8nOLXBfLlzF@!V|#gI8ZZL zw`!!lFD&W2!SLP@RYDgMsKIMk11>H6?&i1@3J`r*FtT3rpEygdN5Fcubt-Hb9!t~1 zI_9LNVKbEuhL41F2yn&o(#wKchU-G#Qpi~-)h%WSv5IWVnKMCJwyhKc^Z zGC!IhOSF&jLW9@Po9?-U4mtTWjng1^!tLi6z29eNnjdLk_TAkt6-{jg_|?U!Azq-~ z1>5&~HlZ}IN{ba)w=)K~z*T~uC~9;+!l6(Zf?zCye-Gd*k!GjG^~Ca9Ce)b0YG!%K zXBg`5dmhn!fB!?_k-(N1%zvvmVNGqZDh_@Pdm5q>7RW4CZ`!Pel zYyjV6c|FWBL7Ecfi&)REv;edb8>{_L7LQr*_hG&V5e*yKs0E4EhAn?(C?=tFUph^^ z<4^a$o|5UHjUy2GG(DcuQ43V4Fshz^E z^ozW1C)po#TM4qbT+Gd_4KTPpp-O~H)u7We%8uN4Wb&=$dRPaT7Z}vLm)q7Hl~V6}wybCnO2P1* z^()y&@))@P#ew9puaP|z5O7A=_|_c^UAiA6K=SuvVJ`?#rNfF{u4~5YDzRuq2mI0so1&x?cevr4g2Kq#;+gap3Jybd@VUH z2Cok}t%;nz!;F)?LGS&QofWl^;^d{3wdng7tFjvvn-)kIA$f+Z^m_jJ#@$7bP0lbM zL7Y1eX2x~@^jx7FQir3fAYk4qF5Um3UO7A*9)P8CXPiwc5(iI0XPel>&6lXcWh8s` z;*z}GD{^5+o&9wQbJpLDFSRl^yQwznS{QNcS5z$%oDYx}GKG${i3XvEQaD z$LaQ3ttb00Ucc|}Pm+!`EAN33M}A8+B#a2|dlEH)kz`$7!Ns zqyErKed;6H|B_(!mM6*qG5} zWCmO{ENc8sK)q>4#Fuo}oo?>JA!>YaKJ<$h;ZR6FFqVB@lkwyUXc?%KPvjTw(W)2f z#u$7)FaWI$2^MGA);$_?b*uy%P@g!C`%y8>6GbK1ELnutWg8?@EN^^Ey3+duWDzv- zL`iM>*G1cr7!g#4U`i@A{a3(r^3HA#{|aa%pWLTV;;PoL!Ozuhs_HMPi`j7qk}u=x z-A$as)$#-pDzpDzcyg>gf!E8VttNF&vvplY143`m6dW1(aBv`I#_&-mfbV-uMmL1{ zA$t|26Q-m6OF`O9Kur3*h|HIV9T8S23t_-cCs&pzEolSagW=HoYUXp&c!2o3ovvoC z+?DYvqgjUF#yU0$(6(S$KGEsEO+gPMQE)$tXyF{h`{*y@Zuo8bTuNMa=5^vXRlrU? z%KY`nvUnCgj^-az0gNC^aQI_X2&_-MV-$6!jbBU^bc+ic>C*%6t9hx0a6fsGL5c$k z-wTzn!)f3#K8fZp4)ba3$KR%0wi7J6rHsu7l7%1FDn=(B~!oeO6q(iz6j= z>&9%v))XNEU;+OPY7O6n`Og$5ti#;RSD#5w>)F+12BLGJ$=~z!npnu(DQB~zr~@k@ zj{F}PF)-DHI%|33{| zQ|{6|EX6@bk~*j5Iu+wI@qg8DhQN~?-}SFH`Bd2M1~s@kThqRBOd09PsJrpl8bC}! zy>j}4>vE;7qzK~x$UnKW@qsl|`?t9HC(b9BTQfllW^uw8tb(cT`-d!R!P^3ytT{r! zpe1+jn}4K4r@lv6ExE764!>z(4V&now*lA8&;}&+SeaiyhVOk0%fAQo^!*s=cZ6kZak$>UFd1);*orS1nJ_ByhL^W%uQkUrrH4En)ueSxT%ao;3z32!L!fesJS)cF}nD?J>9C%Kd)U?WpNRNG79H~!o-{hHv zjQx~^jT;~G5|}0~XT@0dqGjbQRIO$o?sdT&o(1XSy}TIIkBVo@$I)yyD~b9* zdMIyZg7iw?!eyl{WKR+v~HHfV7t$QUdwT)ccNaRL&&Z{QLcKgL?mnx$+mmK9}CsWwNQ*GQG8a z79%pbHre3DTB~5OEM2Xz-VtF9bh8iXuM5?d!TbL4h96k=Y=sjzC6u#}INQ|e3-^m= zB_xsTxs^}&N>38@7E@!(?SL*G&2rSa@by8IP;W70KPbQA9V)%p=~_~3k7RWSsRl$w zL1`-7))Q-Azi(o&x_dfaRx&cOLmRHHhUCcvZfXEjC=9D*IJ93iRKED7iyW}_xvH=Q z0MzF?`_Ar9eGY=gEPw0`ZebMj-Dohy;q9W@?F9Sp1o&xi8w6cLneA=2kyLEtbJ9#g zW-s^>)9nGUhH0AtWMaXRG%D)%w{WAqzf!h(Xn@!nDa^Mg@(Rg68oJ(ar;*{W<6ifQ zXlADgJSZ1k?*({OFi$@~uCuX#oM-Y9|D9(r-qoP%oqR0q-o!M1;vG48n_&NkfIFBu z=;fWXhHQLs5mFL8WsWlRJ6ReB25M7!2oRpIZ^z!iIhTP=KuFbR2Jefr7C&itF-f{M zZxKhZ7Ajc$j$F1i)IA`H4De$cwn9Q*@T!5KFSQ_>D%A6Z=CoH_Whp?8PNf@4rx|hC z#+yK=U_nC~oRc3V@sM);8LdSXHM5imeZOdB6B831^h<5PVTenlsNbURFL49WQetq7JND60b&FOdiM|W1NLp?t+ zZJZ8BujjF1`iVB^iz9l>U8enHiLTGaC)iCps-MZgDm~`_*Ml zzhE*+hbt|BNAYSqs;?MS6ah7+FOza%U`d}gkXywwHp<=;%*T;1T5TVu* zNK=x$+vCT;8Ikqd6*-iwvQOw6ZF~CJci3hL&=Tz`S-Y@-$^yG?{*hO@TR{Iz3l2Bb zqb95o2Ogc03eF`x3^*--PIDl|6%u2+T26s zz_R8#gWZgx@9WN1LS+Pc=Ak{gE3--C8m^A}RF3X4W1LL+$d_-U9Dw6Qe_*`iX~i%m zuEzIFo#~JXt+5<2U{*{Y(4jI*vtbQ;{n9RTTc74cFtbfT2?3WFMd~F^BKc!b4#~dU z`TXv=iQTgp7H{c#?8>PXfhsb)MyupA9EJ(>fM=sis*DP;mrc=FHB9YX~wK0-y|&G5X(7FX^T#l6O5(ATA!nw}7xfJzCYp zAkhA|`%25`wVd3%anIYRg^!gv5au9OS?7QKG4gPbjOb8yW*JaO25jLrEsTu@1lNh@ zE%cp?NBFBFm!TsQuL1PTK+M+;NVO<8=TMIx(VkfGMDf*mBp^pnV>FudJBjvzBW#s2 zPgO)0@4OwjKl-v!&uoSr7^kerF+S%;91LseT~8bwEfJhDSSEf0fbdn9n%}=$sDbLu zP<$vOmGw4`F@aAA$6sH=PKHSz8q}i%nk7IJAQM`{yeZbW&h9`g3qKt#tD~oBbiKh( zAR$mWjUMFUKn=h6tZ@D(@*;*VJTe4eHf^M`YyKL)a<`Cp;8 z_23>OZn=38ndw~_1i zXLhiCF_xPn%N5WQ4wFp*Jpq^&vTIcH*2TNr`WnWepFyY03 z1Z;TwFcl*PowCgohdmLy-uTS6O&8279f88{!^x8yN(I|QUJL@6Am70Zm7h>+>rXs- zuhpYVH#9-Iblt;z##@q9;{gO1l^H@7)z+tFsg8R^s4{PbCWz>khs4wL7TS zFnSkkHZ1bJ8mD_bF$^ohF*3}f^+2yOASu3BV{V$_SfH~bhIINF`zGNoX)+tXQL*YqwamwASLLWH4SEiRvf(UD<#0s%N4H%)5%=o8ug5a{Q?t}6S9)f=!4*!J&@WhlM` zyuocN*%HKXEvPKC{5W*x#klY6b07-BTK({zzCnH_r#|OvXvqY9^n?N zsVdIqFzjTYxcT~xjP7yD1cw>M3tl@DhH!$Gq&3!Kp zXsQlp?bZu2&xsLJ+Q7=FaHj2FK2%vcOjQYzJ=IVSEhDq8j)DK=k$Ptm8>iA)uwz7Y zeUL#jPI12UgEVR&sq&Ft84lIYSla1PEI%fF7rF*4Bvn0=1vg)iJ6PhHNm-a(c!tE1 zx>GkM$g`^bNGXI?kr3?0dXJ#GSkXtQWkvDEq1p5E)YGuN`r+yo6|L`l-d zC1FBG6rzZiD;-t8wn#j5c{-q`%@n-Tn)3s*W(2 znnulR1BAdu(Nt8G?%z{!&PEk5)2th6iHr7rC_PXwiY{@3{8vEgNsi%#f6h)gkn0-= z;nwi$3X)K=s~(;t)h|las|Od~n#idT^Fs2K9FYm z%8xAwgrJBGSnmC`fY*u&@H@h~ul$2t0F0s6%ose!UqOZ&?K4c|HSkeaZ0B#(!KW-;lZ$;Dk`;5=2Uyr@ny=W!g0V zOt;vMmjFj>vnn05wkmr~6^|tT@J0|iRL;FL;wJ%I@?}(IqpWsligDOcI%R_r`^Oqs zL3Z?+#Zvu&GqyrxCRLCx?hH}JS(q?t6ftqaNUe957Wddl6?{5TZUfAngMQn6uVhxh zIbSOceKTTlN?<8#o_x^h0eOs>atzaLTAaV*aS7g(bBiPtJ*ATXSsO~%^5s3AwUxOw z<~s|-sglU7Z74VCDc!h%+R@(dk<}?Y?}i%D?!1m;)705ah0&k=D~A;d48dIwB2g`NDrQNN^cwKSS~QqpW}-Cxd4f~mh=!03Af=989L=BKs{q=rM_Jy~DeSg3 zd&RTSf!O;tMSp_l<1tf5ZFZc^qlobZLs6X56*{ohk)(6g36SkVK21M9Vwt zLzGMZC{wSUJ1KY4v+l72(FgI2LCA(+Rg8ywGlT%s`>%Kn7x>BLYVyu}FEuA6la|Y1ATE!;xu-TClGEL$I z#i!fUKHM8=tO3t{V`pJqWTBHzjm8o$|FC!C>{U#dUiS9K-cO7 z`^NoV3ed+8;LO%?uxBN)RGzzjt65gcv{cra_ICZa*V}8s5b8s-NQn|}u1CF=>mhTX zX=J`Oufid^&4k*{PZ_*-yTvqk_;UGvjQ5N(0v2|mL)V)P+Zy6QuOT%bvgYQlMM!UU zR^}R0O9j7Xp2@UHt^-NEdL$3)ATJ}s?KxCEu360px+~?Eee{gvVvat>oK@x}qAoK8 zJ@DAB0F53F6t`Hv6szS2EX)Z7EX<1|#c+1*qLrj(RUlQIt#*C(x1%(q#{O%>aJrXN z-dav}-IKc-L8Hz-0?6Z9zbpvPg#pK)MJK0tL1mGc-q2qgAT$OTihm&x*6PkmfY?df zpg`aT>WeejmH8Rheo%R^UYGT=v2DI0Z}W}%>lS-REqGr{a_)6-1>Lk5avhJL{@4Na zEY5q9b&^jgKy4T3Js`-~1>Z=8sS)nohHcr4xs$=jS5>hZu06b!ivk^j*$Q!iWSsgT z)gN91^N^NFu9_UV!!Sd~g%aEGlAya{a~Vb`#`RS~)TZcJ(m$RR(A^%YU z8(;*%PyUx*%%<-zp>2VWnZ$s9v8eMOCG{DI^)8`-GE(~B_);DWgqGYsMo|~lb7ZT% zdEME!`iv3zB-~T1LEe$x9WU-+a4C~BUTwErf4sTXaCK=5u-)pYr4(RL-1S31MB5|3 z7o4Dr@)Uy~zNn6oMfFVgMsMxykdgxnM%mx(n^1S5S$DI3_DN|6mf@6UGInudQco~crm zcHit3bC^!d2jnhPPyJqw)(hFBt>V<9Z>K1ch~l0H|R4M zfm2B_)U6@KZwH}|o=UnC*Txb$bdNBL`DD>LKx-H`MHo# zV{DNLKf^|QdV;I-?XOwvC+8UfFc5qiNWO?!v-cYRhpG3DYASuBg_A%CMT$yMI%7dW ziVcKN0;niTQ4mB3MVitDLV!>b5K$QcJ4zRo9-7n;q)IPJFA2RS6ltORy>Wi`-f#Y3 zty!}sCpqUmPub7j`dFMP-O{tD6EdJG7++F&Z;)qCThgx8+%9_*2U6F;v6c*Lw zdxEVPR09WkMLmvSr7mY#syI{`gxgOD{c^>I2n+hNzgKn*^}eCA2sW#H#_DCx-A6`3 zH!TGh#vkv`KAqZXOS#ltt2*i?82FM|?(4#E?89+T3AQcC&BIXr2xackO1@~Amo-lP zIJ4Hzf-zsW!E^=}Cn#b>YeHT}Yu+J5{mjjeX!>?rN^HAQ-;a3pQuFW9c^$ERBvmtc z7dR&=mr}egJH<&jq=m7X+HU+`6>N259)*^SIzDa?e9e8HX%`Xnpb1fhNTmX^&T9j zz3~IRMcf(_VO|Hougf}VWKX`&BV@-SCwMh(q6#HM8fZwGwwg+#dZCw@5DB2hn1c75tBoI2 z;Ij@@6-X!=0jL-9%mY#z5A%V7e{rbY1)dW*Bq4)jUeQa6ui?l~NC^7oYvecfZjc8| zxioGUC$iD^J=0)R#F{NH#0Rw#8pr7=Q`aQf*WH-05pd0P(IemG{RjWGZzUp|wV#@8 zyQ=fk;RU|drCInilm;8!PRjz}AySp9qXwCzZm6DR0$;~t883KOnM9OdJ3z`|Tvb7W zkV*FzPR4S@WT8Sv35b=m1$-Q{4;1-4*uMLvv7qV3nxAJ(k=-nDQ=tXxMj{#q`-9G5 zg+?(k3}}$v9Q8d7dp3xT|LJ=!FDwEl6`7DH*LERJopl<__Qo+pf9K@1P=72W;8rX) zw8uS+iCw{l4$a1XJ7&VxSuH~5 zgoOp%v2ll$b4;h0l6a>JVy?F2TCVPxovd(Nw&E5#r^3vsbkrEndU7h%7^ zQyKxI3avRjU#ScN079t;6McP87C*+kZvfLsGVZ#m$ec45D2o<<{)P)>Mol8iHV~8l z^z8U?$+UQ|rDV{qc9_*Ca0bvBuG}!2N<(m<{idG(lDA4J3G_el5b`36D1OmGdT{wl zwLv(J9f0wM9DyTABL_ST@y1e%Tq~;&BpF40spj-L17C^Q?-S{m>a?$4AfQoPvQIXv zc2=$)b|`95G<;YTxnxPN27(CVp>`nGDVHxD4RuVOQ1HbjN8ZYppqI5#p-Y)rhe5F~ z0wBR!U*+6vifzjfs5Jf5XLW2OjLR9RxRK%!W5jR6`p16kja-AY6hY*dtrx0sdokh` zg_>K<&F_X0e@kuH?23LR5<->%%o1X#+Ls=A&AKAFPN9)+s+v4T59&VmS~hN7cPnyD z{=IoelS?Dpw%xr}mxXk~CCzcMm`;KFpPywM9sQ`cQ^CZlj0Hg!xGov}k=gUB=7mB&}Foju6w!s_%%2gutz zS;Qzl2Yl*7|1@VRa#4RN3dQHR%+stc`ik{Og2=Q9Y3xdokRM^#gq3iNUXe5y(zW8B ztS|6-UvKa**c!)v3$`YK-H&rp$5z7lE5klq89Hk46z4Q?A`I}hkaE7-t8po> z#RQS++f{LD6HL9WY?(ZjH5OkuYcQS&T{SJH< zjb2uE^w`<{NIzC2@~Q-GonCAO1I3l*2m~}L7i0QBUf(@_t|G!vWpsfHgc9Sh;gYa~ z__v7e7K~yDm`4@#g*ZNPAo~A}_OGj#8I%7mg5c_EkzDc%gpi6%Q_=x%wt)NT-{-uG zT=z=btOV7*9M)1e>iape6e0SkoquW3k*Yur&!8@RXm^fYr1S-F#TnaI? z-kytIE1AvsiW`wD<>G5;*64(h_-le{r!72+$GF1OC0($8PmAMEz~+2RM(pZHE)=c1e z$~6|_19;}%>W$$>OJUUAdVw;x^p;-zqV-Qo6UamT|>z(eBX%GN@l$`I(I^gZ> z>mKHR)D-!6{VLlwfQ8Se0DCf^mv1p8b||^_8}-$jlFItP`pKb z8Irn)C5gb48zGzZ4qF-)K6T1?vd4kEvEnyutCZRnhb0GQ1nC9N)qC>&sd zu}#gq3*?;QO66%9+EQn{{LrEqNsEF20TPttEU996>Y^#`EtAXh$}>d|7POVgJpklh zG(D(KQg%FSMOg(!{W9hMl?O^+QVj@Na4hXYTO0EDB8M>-LD6uh(O4^qbKB=`T=l|Z zEEI1nN#%?&E?fcQ2`iSZ?LabC#XM4kcw$S~rNlLX6HKHcmS=`IW*6LxNH$hqnLL&A zoneJu+6kbhIoFw@x&P}qJz>QBeb8*38@E!R9>6#je$VLxHI*B* zgQP-FO(v+ba8LR|#a2HJHZ9T0Yh?aD7yBq-^y5JzWdb>&0P{CuHrV?f>dbO4+Nr9@ zri;#G#So&%Gulj=c>|2{_U-Eh%+blD_o(1YVw14xPSCcT6DnR6eYIrAV8z*&(p>#- zZy1DEu&v3DMyRoGR=%54pQHZKL_pL|)Y*j%JLK9?YlbTGPg?)+P+g9ZB_4)ij?x$6 zb4Yk_yj7D8Kb$)O9`GGlK$`hN&J%!q-cNW*kPn_Sb+xd_53nl+?jq$^_0dDGvg2<{ zD-%Gs$~}1`@3*wJwfS3#{S~U6(v|d8t!CWi$A8nH_<@y5 zI0~q3{8NV*O5MG)&GNeQRhZ?IN9U7XCsSi`6dn$_0vg}nHZ1IOCTi(I7CPKtHrtoU zF#zs9vs4tLy_su(R*&M@n=^CBX{W9ya1Q`Va+}6cP5);<}Wv%;2Az znWIWiF;=0aBIAz=9x24Q(ARYWT$`wmy2}H(BiEyf6o_G}-TF3+*4`oLY-#m7_^L0o zoM(E$wP=X>*r^*R>oP~LWvMay9e6l6w97J_;G-hM}8! zN4m8JR0U=;Y^b2uahTCiWVE#h@ml2*DsINwxv2v)y{$RtPlf0zFm%s0FI=fqz8X-+%EL+nM){go!i^`nS_%O{6ASyvg#}_X0EY`WA^d*A5pEBv4KM2=U+-dvmUrdpC|;`BVso?l!a83JaQ`^m_{TEc z_t=v>4EyUcqoukRgX|j+P=Y7I{DqhIBw(jJd7w(n=p;n&gT~lFT0!|0-&g-<%?0gp zE88%;rzCJdh<-sty#=ZN5~Ea}$J_Mp zk=sZ8pu^Bd-(4-gM~kM#lb1@F`K*xGbnsQ*R}T99pdCEfxBv*=T0;Hb608KW$fsJ- zLtWZAzECgwiGPmuK@;v50qumb+(xXqzk}9(f;{=g>@0Vg=cOMzQA6<~-J17EKS`te zk2r^O@FQ7JQL!}#yyhBtHdtXD%gc62VEnP`Zs+UHgvyJ!4u;aSi*B}DJ~eM6-bOR; z-JQJGZhmIPM7l-a#nwAeAOxG3wiIm5;#|wTyPz5?R5Tb_W7$9dsahYg7DJC`^%4Cb z15Xe_qeQr&~?v#6dU%GmQ=I{53dt}am_orEZBj7>|LfaCr)`umn2^Ga5*AnfN z9U|iME+W76Z7?xx=e$Qb0CMwa84W^N^KYJY2oAw5jGB&8SUq8Ne^mL#`I^u4Q=cMA zupv%5mA-YVhiH0MF4Z=?-^Q*Fs1AfGVEJA!+fUFeZB?QLlHa?5055(YlvRxUK?fIg zc&h}{7;`K_f+#^j;x=4`JL?f)yrk!-Xj|q3jFl|8NUCQo;}}*%j?k{T^+W#)Q}5om2mM&39+W!2<-cH;yQlh?S1NP5^x}uEFl! z0=|rMO~rfK9(aJ}X1zjL7Y4fkoVK`JKCZI*@N_^Vr6_m!?OUP3z6D9?f%(f%mF*`u z>!8c!Q47Cz?0djrn<$?2U5E&9qkS~(!@d_-)&diVM4tWoUVE-xxQ$TIP{=%tNR>4?$9Lv-qZYKkpUMo}vD+kuJBhhoh(UUZ0!yxp{-@wy@^83F!RSQz(htQXiw z_iKgPd)3cu7Njh~R0Sqe6q%&)DFXGGDXGa3X!K3xqaVB_Byvt`p>(yZ10URhmYc1{ zMbujzmrd(#(#gh%G-mL5K+2@+pod=P$t@-qPrdMsm3eY7r3@V%wcX`=RLUcq4Bu>W z@Hdo@#*;Xxrud~+4O13u-{s;#r+#l2jT;d3>=qU?ipvUlkEk5t%j+9kgpkVhf5A8Y z83^dA#Du|fX`z713i_;bHdZH+u|ioCHgBKY$T@e_*tCMdCX@s)P&?`KyI!b|zym-x z7iHHLV8CkCH+5b~IBw+Mu=_q}`n^aBwVL|o?coK?g2oD8OoZbE|5J&jCw|AON_xXT zlJxOv1^)Q`WBxPiM3$kM;XJIi<||80YlNFzsem6BegcYczZ$GcYdn~AH-dPom4RCzD5h#Q0%T%DB=@N>K&`ukayVSjdDX4MrG~Gd zTXYjqUKGB*+ZQCTc~}Cb4bg}B>W5(F7g>rHh>V?|+uw>7wyxVt%#(9A{JWDW#)0k# z%^MUaxe%d~rSD|n#_ z=*>Vcr(o9kjY^=P^8oE1P`N$v&a;58Wmc)Z>ml~G^i1emJS^gh#X-f6ss$?VOFY}>T2M1wVr7uuAM}y>vA~P7@gkb08q?77V^7i@R@x;j4cR%y-;@?A zaBDUU&$Xu1h=fSMuu0kK>~!cl#;lmy&5`>sb~Ps}j**o{!z(kvaUDOu7{V7=5OAr} zpD7elr}lIo8(rOL{LbP7X1>aEMABk223S`r&4Q`YgI`RB9u4>hc5(75@s;m8j8*vE z0sbFtKwQ}M8BOwa0QPwuz^$(9Gq5ajDFX7flVc+j-;8$UA=m?16t3ALVrF-eEo_NH z){Djp(ibYr3IK;qoNKa43j73aD*pqEs-2&QCb@!zX0PKJkCFB7-j5{;)6W)Sz;h2iCeO- zw~<3K8v0x3G@ml4ffkJ}wRCkoKeZ-R#j%a5G9Fzln6$(1DcrOHh$hiB!MQrFeDnnd zql*aSOgCmqLqI!|q(L4t;z30_n;ZWz?;p~h|K7Lf-8gb~Pc04%EY~~AlOp~|_^NLg zDK_4up5t!Xv()yg;b^J|1p}yrKPq<@{g$<+qa)x_YL6k^DW=GyvyGjy96nsFZfzbK zXgXDlu4lUFmiwOFi?c2Wy7;67k-qaz(qkEiq9T^zqwXz(Tw#>^wL4C<%Usk#;vyX{ zEk-{i1nBL3gkd)CpZwCw_&L0MUn=IdOYiu|*1lL*k~f*Sea~48ZD!5;_Fk@;%jSwx zWKvGnm((BfH_Rd;x&z4R6I{=>_rL1Ltt@d%Pic9|Y8R{)v#KU~KK{{8JtQz)El84= zE%C|4g;^ROW90Yf(0#<}PD4rvLx}$Q#}~5WwL<482vWK z;*6hkH?IQkyb_@5c|#iEKK(u89ho708-phs-^#6@?+j9!|9(E`oqW>>+)B6af^PV? zGp~_ST)b?Jy|>O0s!I4`BmZb%7l&Pe`vInBlfuqqj+p{C2gq8Lfv+(+-Z>xVG?j?) zaxRQJurlgu8D1*S#b7Pv8cy-t_d6c0##)N<2&)iW<`^@8@9aFsQ{LvtCM;i(^K&YU z&tai@XIdUieh&u}saug>YtHaLCidM5Fg* z_FmGGYKoGDl|VhV@5$+%>W6r-DnP>~i252}S0@zDn` z*2xV|0EMXnf&20AQEpU3kZf@A969|@`&!4!cV76H8|C{f>mwhE;hDE*py@;On?uVG z>fM-P7144;lAJ~Hq)|jnk#V8-q0V~IyMD|6=y;syRCcHI7_}VHbjpHHw=5_!{wA_q zX6LQIGD%>*6>txkJTi~TY0vw@TQ-8xhr3-Hk_QX0dee(6j9D9n(-v4Q%J3O8`R*Vl z#qVTt%lV2aH<$7S=VI_MoJfN|#ea>t&-?T>_3kYW!XnzS7G@PP_6?Wv4Ww_5y*@#I zWbS#*WURVs>PRE6_$*P^NnOK5ru9+HJzWTe)!RjyWE)b(J*^r^QI?e+qCG&k}V_eFdzR5t4`nzEBrX;Ud-?mHyXGm zV$hH31-K6lRRHUX&Sc=P)c0j^T^#A_N(5H27apw5{=}e}TwZ{b6$bBZUlDR%P}M~# zw<2nT^T>QdQxM;yzfNYMML*2LeRf++S%SKXDcqFs!$gW8kGIZhT)q`(ZRK{FPn)ZJ z8Ixn3tQ_!Dtb6uB89rzVNqTv=a2tcTE>YPbB0L0I$Ha;xEjNl36*P@t#Kb6 zB0-_^?G>e0m1u$Z!(UngqnnCoNLuunFPF>vS)daJp-aPA;t!L`nI%M2Kn~X|&q!V76^M*u7F1Yody{jn+%pEWqSqI6ygb@YYbr_1=F zv4TMj#h)X5-(IK*6Brj;R z^$G?7wcEn?g28;*c)8eb+kH8%)@OpG5L1J^K6RAUC6P2#JY<=_bHAZ*{1lw7AER&p z8=bAL(JwYo{);2mEw;;iS$9s(Pp{DOPOGztpQE1_ zM?MtXG@Cx%;=@+TsV>5bG2*xBCfsJ%9Ufo$c*Y~i6>t_QxR?{|hPr8_OVla*BPJd>1+2FTdOF$nwJcB@APv!!B`;)}w-4?uY9n zvH9UPh&6i?Jf*r&QU#^$el5<4Cd_5k2lvRz8u~bOYG|-I^uKfND7z~Pd{=%!)mckT z_(%}fP$Dlr!>Ic@{?MU6|NfCX{7YMN|95oU8lVhzhxb)UDFV*`o2&f`@=O*Q?+L(_ zaa%GHAS`lu4;5dqSdGiDGAQZ~D)2H+81A799-fq`3o7so@l@8?NNRU~ajJ?JS|N{P zgp%Z3q(k>=S@U!|di4b>a)|D3?_@dG9eySU)2}!(Q><90?Psnq%?QUt;vFAtkKmcZ9HShulE-2%f=t@d2_~d|M};6 zXb`RahwY}B)%Wv|X_supl8fIz%uj(A@46|l9=n1vcw8!fn9)|{Hp&BC)a0#@dp=%8 z<@R^pFvy?E@U7tO*KzavRmo&aXJ;WlPTkj{glyo>cSi#)rW^p4z)=%Xc3*1_&_{8# zJx4=)9debf&3|S|+wJg6D3Ii~%_=$bCdYx0vH9VeQT@v-o4Dv6>RD9M1rMjdSP2J0 zSq3wRzWt$WhQJw{iXWUD8z>DCbcgv#-A#IZ7oIG^vMgUZRNyPqFY<8)=IVF5c+sv} zWPZL_>Ow}cxL0BT&st<0A*wZ(Nsr;#DL#dVPd>M=hqWu6x}B2ElE!H$b~A-PMu~8* zrvt2Ku-shobITUEaIv||@a*Fsea;a^I9`~lfq7Ua%ts;?S@H#dKyu7V6Ci@8Hv;Su z)I=xqCBp&|XEN>6(!&F<)=IeMiKO03K z0P_K0I$v8U2|PpJDG{^;_K`M{PCul~^9@e2$JyL$^TZr#bC|EwsJgAb|4pwZfUnju zFx&U6lBxS-t$ZlR#aA%tJ*bi5tly_dO{>`Yh%IcZVn{T{^b>g|R%aW7aGdacN`ktx zVA+%ZoOl&DCc)`1Hkw)uT0&)f!h>3zBkd{|)yPbF!OTPY|CGFTaqoZ=V#=9$qr({0 z4>I^+p~hl*k5F?t-ITF(2 ze8gF&55d+VMSo>nCaA-gyXoqu`}KiLT&Y5ySu=vKbswefy7{j}ceKP81R&i+bFW$N zue`@xg1TxL`BwL{IS73Q-j7#aEpNl#ZHg^dR-F-Rbx6*7;gu8WpWY)c3am9_Tcr)N zx?p0~XHM2>-Gw+bw5yl&37xy(FqW7cG_5uCOSH|SJV2%CKGuGUA4ln?JF=9?itvVC z5}<0}_exe5Z)SRS9)^Hacl`tI{BSA(v?>iPy&OiG#iHqeW9S$WaPYsfmdVUt%ix+- zjXkCn22ssd=T`fjroYpxLz{o+sFDP|P-ztX;mZyLK)3o# zxyI&s`B-44xp^0Q0p9_CVAh)rGk&iGE2$rWii)*%aN#l(|`IACYDE(jEH+_q-eE-m85R?LYiPpLt{4nw_s9~*I@XkZUfR+t4h*e;EJyw;R{LZ!i3vMMLGCO% zB3j*I1o<|?zwJ`>554HuUI~lH>%C$YjQuuPucRW~+2f7QVqd%Mx&c${>uD+4X+{@$ z-7vM{Y2@IO21aylvhfjDUMcK8i|K<>5sUfN?nPth0o9+OjGQc8!uG8k0_YeVsw@#c z@!awwg>EQ-ec_F}zlO&IW#M_kzj1dL*dw$A;#sy*Se9Ly-ed{jW%DjwX#S8Vn@H7sx2rGeBCh47#S#ukx;90&hx@gy6Tt1FgQj@b_#qF)FgWu@!bJ+B(>{9oDkoGg#cL2502c#*}|=P-5>RK_{s3vkAz^`y%n zlCk0}PFnLV_GTx<=XX^tHuqw7dW!@rh+K)?XM`l?9W0jAFQ6K@E15YlN>it;{mzk= z59PyUQQ5;nzxa)eVYy)^)ylf8s)oDS`{qnZv3fJyK585STj%bx1kFQ}aT0`i4(}mx zZzoXBM53MYWyR>kY&CeJ&bdcobxq3~;cLs+RtVp@R~{drvNKq1cniib%x3_QY&SqM zXL*pxxi0>mvdH=$SAY2ngaXK6hj=*~Tc>^~?|PrPX3Vmrvl!n{8*X`MmS{>ka+=0k zzN;YFFYm^(R$xnQIN*&Y$J^)Q96;2haCmjN4j&#b&W_`x8CsVrWTZ1`vmBc>o`xL0 z_lw>0MsqOh22WI>%*JK)j7hRJZlQ>q;VLj|l7)U!7DJrl4w1)93R~O1#u@RQcl7iJ68GZSs>K8* zmi@I{^$Sa7fswT1+)03JtQsZn+y&E3xXBfkc;Y*&$GpXiXRVfQp~fO(nXces!wt?t zH9H5FaQnMk7m`cP`Ji%HTMfOchJj%G9$jOjJj;b5GRcor=NmP#`NL@R`2-sg0=-}Z zD`{8&Xig{t|F@Dkn=Gyv_yaM2C5MqHoILRr5QBr5L{BqYd5figkje|*X*HB$Lvc;R zMdmSgnxXVKM{F#Oj7XuP4|wY-BQX{qAI--VMeaFDw*opTdn20pQ!n5f2pdlF14MunlL}D7#~2V{-lrAQT^!`O>=z={w?N&UESQG!V9#^DYMz4KLo}tjrz#vRb^$ zUH7VdaqC=hx@||9JQ6rw<`fS?fn6YTu^w}KN4ZyW%O6gHS zT=Fjy0aGkMs053~fM<(eTdXPRp=%b{5T&_<6c-IKy_sHHd|_)%Nv!qsfIl{W5Q&&o zUevcK!qp6aYXHLHY1{lAhbh*Wp>r#z_|l;9uz?P%K|%mXCIJfhkxR_vXTfC8Hw=SZ69Natk-b$!1X5&67_)msI*WME3-n7)MJ zUAna^zreNTi!V#r%6VE(bOiZaT>#9k7;uJdYV&IadeE8ef6Q?Q^L89*j$FaHy`gD7 zRV7^AI@yfG)Fnk2K=DTcXgT)DVBpk4w`41;aI(~xuzYufXM)Qc?Mp@HY(&8VkX5@L zXTZ#wt}jN9%C0!oXsn~gKb%CG+&{$`PX2pv4`5~br-YF`Px>5aCR|igGc2~sjDP-M zHkLVq1_+qtG2nYZ`-lg_n8naCP9OilIF3n~zqhRrJi!v$D|YZSsOA=Cs$+n~?v)^?Bv#OIIpBxkk2F1L z)q$H1k1oT%D~tv)q2QqE#kd?}IVKKMk<)f~J|{D6=iZXZ-zp*?=wPxLxMa=ny9BHm zvm&=D=n)qIv1Ci7O%WVxeRGw1tM_pfHLXRKN5U0d5Trbv5>ryG4)4!dUkBj8Fnr{L zQM4_YA?%%Y7#!wcrmO_@9+4l*e68MVfj?jzg|~En9BErbV_GX9ZFt7^B1ekU36&2# zifn_~;L|0}wg6&6*iR88)0hKBpqL=gfW>oX_D}KhVd6hAX^=!1R=R(R=$GV*o_pM< z)M`$fPk^rX_mY^965x-^#HQs9>MH_$$DMpIi~=M*VCquPBHv+Vc`1>m88g%=*B zP7^af@&+xqy@B(pShlZND?l~w=fA>JrNHza^yDjiE(x4o&VpdLC0Ju8u6K-+&Vi4H zWA#?!d!7J~7AXwI0ueZWRfV>EYN?o|Q6BMrRS zSRy~2$he@Gy;Vore^#}Dr(=(5&EuEMEm2m=)5Z?g+nE_#= z^zx@FD%6jY8A>f)P^2yX@ah-47mnt%c`dN7U+zUR3&P;lHf3dK5VgV|BM0JkHVd2TYY;*AP&wRA8 z!epsC_)@Q%;v5K|%sx0PmmQb(WJ_9KQQ|A1toOc0YjY4CbGVU9YnauO^PBYXS6mw> z8-?)5i*_XhFtbp`)F9{Q!FPVALq0&G6lxJWN=Y9VZA-U6ivNVlE$<<^Jhsb1^8ydy z-(?R78UbswH%;}DNjC2AfS2X|A~pYIGbwZbGcguY?qMac;oO^npZNz+9xwndfini9 zR}Yi}OzFtc8-#$dMJ#;&*8H3ajf@HtWm3sNGGopP7YAejoW>;0JIw$_Th2x^Gy&$# zWY5P}IUBOB5)uK&Av@=1XUVC)1gd2kYdBy9*B0!^%iN>;_5>z9&bUP~U(55IJ9OtALm_VNQ<4P+0q! zTeNAYB6i_CW6{2(w}l}D^4L}2$ssWQ^}sOiVD!mDVjBikz*MnqP|PN+x34W>S(uqy zfO8HPAs{oLJbGO`Rm-657n&)LXIJ*(?8GPnqMS@?OTplj)VZ8*gBZo!Pd)Nixl2Sr zt=0eqGAH0|CYKqf8(tPsPwZ`O+5r3zagdrT4whPzer4z48aGtsJCV?(=3y|SOJoM- ziQZygLck{?qm!-d^$#G9VrykAQfPp9Z42l@k@WZ+-;X_39LYOi#l%|wP8+Z>NEuTE zW_A2HH1TDNq_oq7ATwmbHa<5zk7N&$s1g|?HVr1&d>=mGt5>}EczYRqehrbK&F$sO zoMOpz27wUP*peL=NgqZ`Qz#%-2DQeX{g8bZ#Y+IE?rhEmPk5QEtOBQ(=2IGU5yntH z^c02@8nq*!iTOMBG$8gYG$+;@8M>2&9>#Rp&s8!LB-n>~=5D8F$2)>SQ#-h4fHFSd zf2TP8&H$(#m(FOET!|EX%d%z^8;1w8 zu>W9n>^;|-a22AKB1BId2q2-z!0+YVy4B>ck3@1B9#FGep7@)C&(8*;k%r>I`nljQ zEM#^t^fvS;GJ{X=ykJ%oz1bCz#_QlktbpX%J!SQm9cHPP@9URSQoCrOjHyP(O<7Ww zptVl-d%IHaEfHqEwxBfqVZhf4Bq@zq7T~V4(AYlU%o_%%Ps{=N8>>$&fa`X)GLK{S zhWlwX)XA%q3iejbxu1|3=iu|T!wGMJ&d(_3wd5klk_TGAEQLAM<}cj|1#c%!bqwf| zV~q^ahKvF#*P=de(=WqB&k|uJ|T7$ad^b1du zEb?2->Rsk83=!}EsOi6 zh!{kp{y-_`BjKuAJlaI5QHVei>Q^shir3eZ6CIz&=V-N;zi2yl<1bkw4i8#lIP+i~ zop!kU_ogYZc?WiEo~hg4JB|X>{qvpc1OCs68!}bHJx%V}OpexlQ*n{W6o}^hymV?a z?}}p;z|`;80@DONH;QYunaF%oHdZ&x^7$DGlNlt==`(-4^jx;@MWj;}WyD!1?B7=~ zb*0u6v>=GR-nY$g&oHas6hly;v;>fEj%8DG5K0{O`_t%t`-SrRc&gB%Jd-(yX0~;} zV$eZby=>h|@5C{;W-Q5Da+_GsdVLje$T!ND8kBnk<*cFgjjDURc!_qkS# zAnCj;1L1$F4kY`40LwCz1k2*dJY}Vhi-3MG2(FiQq2*Sxm8rV=VAY3CH&=MUn(;}h z9UMqgmZ)SB$1InSylbgb%h3S#R^xjM%&dhgWk#%t^V(am=S?w%#e4TIH`O<$)-iMFj&_^J-1{;V}a_uq|TrgRWAJ@r=>9# z)fw^RK9Ag@J}POP$J4f?hHKK^Um!BO1!cH_v%9xMNN6ws${*@k+){7 z{zsqARDN=%yI7EnuUt*dzIN|a~O*TFd6)&4;L59z~S`wCH38p0h<{0^4> z$#I+_R32G=RB8st-S`iW`&x9g0um}qFFe%{R2v1302#Jm5LaTs|>ty`7PtLLJKT(<&#UUwzE zV+}~N5og&3OVodC(vNiFxL4hEXD9|meb7~zjEzA&RX^4VN`5=5mdi)9%h=$l-Icge zwJZBUk@q$|{FZ;DB(GE=eEA<61y|!MRSJ+mfzoIMuIGXJtI&=(v!&zRq&EQ`Whmn7 zTLF{Di_f8!j<;q($fq+lvb8T67L_2p=amLw0VTFp>}#g)G6eW*FvohRIgM%_m1-aF z^L|>%UJHB1O|v9O1~8V zU*tDd4$+JP!0T^4*j~XttFKo?3tmWc&Q<6kX33N~9f<9m@5vfM1W4PqKhpw*>$CQu zOV@Maz+rm-)uCF=rBcnb+KT^e325%d%^?zq(JPPKzL&-q>}v?E+!+&x;%+?LJal?f zHN}b+7%yTR%n+WYM72kp=&=%>1zNsq*(-SJ!`K-Z zx#g$-fC)}{x{VgsP?2LQWR&sFbudqrBquf^k z9yp7B@ym1mB5n3iZ~~*DOyd#JiNdWv=aALlc8N!}tM^#(wjRBFFFDt|(6NHopO`3m@J0!&WC_49?UOl?Va{QJXOG7+&TW{_j}MaufnV!cg=L2biy z@2c@KIXpl*lWKAAhv^Bu+`I?~-F2j_1bW$Ka;KKh_C9sTNtN}_Z?u&k?}?1VG&rK2G1392%Ub7&ZRM62 zMoVgW&3s}BW=cKRwU_Kclj#Z4JlfNfxzjNJamQMp=xW2G~9@I4a&P&^lZ2ceRHFw234(bEl3wHxh@*o?Zj@7+V(uX zle+gG?wkp_MDHse@#u9-Qk<8#>a(vuTXt4;=?6+#EiPVUHlCJ~c)fjV?0i!WFWFLf zHUaN%{(qjkTo3W0h#LzzPEpq#{G(rd1Z+$4eyo!VTl*&#`_KO#nL_J9#_c7X9P_16 zfpxaEwdEw?sn=tzf!gaWUOdddXI71E^<21TqVtkvCOT4)ix3R@}~

DvqWvlT-b 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 W6UJlWNcmv^0000b>lL`+}TyDhc#WI4t+{*GAv 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�UfIBJB2xTK{R2h$^8xjM#clm+fmrSdR{^iuT?5L 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 Wt!ukum8r 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 cVCt>Elso6`FIT!N`ceRwt` #include #include -#include -#include #include #include @@ -107,26 +105,6 @@ main(int argc, char* argv[]) controllerNodes.Create(2); clientNodes.Create(clients); - // Setting node positions for NetAnim support - Ptr listPosAllocator; - listPosAllocator = CreateObject(); - listPosAllocator->Add(Vector(0, 0, 0)); // Server 0 - listPosAllocator->Add(Vector(0, 75, 0)); // Server 1 - listPosAllocator->Add(Vector(50, 50, 0)); // Border switch - listPosAllocator->Add(Vector(100, 50, 0)); // Aggregation switch - listPosAllocator->Add(Vector(150, 50, 0)); // Client switch - listPosAllocator->Add(Vector(75, 25, 0)); // QoS controller - listPosAllocator->Add(Vector(150, 25, 0)); // Learning controller - for (size_t i = 0; i < clients; i++) - { - listPosAllocator->Add(Vector(200, 25 * i, 0)); // Clients - } - - MobilityHelper mobilityHelper; - mobilityHelper.SetMobilityModel("ns3::ConstantPositionMobilityModel"); - mobilityHelper.SetPositionAllocator(listPosAllocator); - mobilityHelper.Install(NodeContainer(serverNodes, switchNodes, controllerNodes, clientNodes)); - // Create device containers NetDeviceContainer serverDevices; NetDeviceContainer clientDevices; @@ -245,54 +223,6 @@ main(int argc, char* argv[]) csmaHelper.EnablePcap("client", clientDevices); } - // Creating NetAnim output file - AnimationInterface anim("qosctrl-netanim.xml"); - anim.SetStartTime(Seconds(0)); - anim.SetStopTime(Seconds(4)); - - // Set NetAnim node descriptions - anim.UpdateNodeDescription(0, "Server 0"); - anim.UpdateNodeDescription(1, "Server 1"); - anim.UpdateNodeDescription(2, "Border switch"); - anim.UpdateNodeDescription(3, "Aggregation switch"); - anim.UpdateNodeDescription(4, "Client switch"); - anim.UpdateNodeDescription(5, "QoS controller"); - anim.UpdateNodeDescription(6, "Learning controller"); - for (size_t i = 0; i < clients; i++) - { - std::ostringstream desc; - desc << "Client " << i; - anim.UpdateNodeDescription(7 + i, desc.str()); - } - - // Set NetAnim icon images and size - char cwd[1024]; - if (getcwd(cwd, sizeof(cwd)) != nullptr) - { - std::string path = - std::string(cwd) + "/contrib/ofswitch13/examples/ofswitch13-qos-controller/images/"; - uint32_t serverImg = anim.AddResource(path + "server.png"); - uint32_t switchImg = anim.AddResource(path + "switch.png"); - uint32_t controllerImg = anim.AddResource(path + "controller.png"); - uint32_t clientImg = anim.AddResource(path + "client.png"); - - anim.UpdateNodeImage(0, serverImg); - anim.UpdateNodeImage(1, serverImg); - anim.UpdateNodeImage(2, switchImg); - anim.UpdateNodeImage(3, switchImg); - anim.UpdateNodeImage(4, switchImg); - anim.UpdateNodeImage(5, controllerImg); - anim.UpdateNodeImage(6, controllerImg); - for (size_t i = 0; i < clients; i++) - { - anim.UpdateNodeImage(i + 7, clientImg); - } - for (size_t i = 0; i < clients + 7U; i++) - { - anim.UpdateNodeSize(i, 10, 10); - } - } - // Run the simulation Simulator::Stop(Seconds(simTime)); Simulator::Run(); From fecbfd35a3d8363d867a8981f7022bf8683b2798 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 09:28:19 -0300 Subject: [PATCH 06/10] Updating CSMA full-duplex patch. --- utils/csma-full-duplex-3_38.patch | 86 ++++++++++++------------------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/utils/csma-full-duplex-3_38.patch b/utils/csma-full-duplex-3_38.patch index 90f9647..1127b84 100644 --- a/utils/csma-full-duplex-3_38.patch +++ b/utils/csma-full-duplex-3_38.patch @@ -1,11 +1,21 @@ -commit b554673c2f54e3c703545ac65316b854bf5378b4 -Author: Luciano Jerez Chaves -Date: Fri Mar 31 12:17:46 2023 -0300 +From 8110d79c7bb6e9f67d271e1bdda09e55c92ef107 Mon Sep 17 00:00:00 2001 +From: Luciano J Chaves +Date: Fri, 1 Sep 2023 09:24:38 -0300 +Subject: [PATCH] CSMA full-duplex patch - CSMA full-duplex patch. +--- + src/csma/doc/csma.rst | 62 +++++++++----- + src/csma/examples/CMakeLists.txt | 1 + + src/csma/examples/csma-duplex.cc | 105 +++++++++++++++++++++++ + src/csma/model/csma-channel.cc | 133 +++++++++++++++++++++++------- + src/csma/model/csma-channel.h | 112 ++++++++++++++++++++++--- + src/csma/model/csma-net-device.cc | 80 +++++++++++------- + src/csma/test/examples-to-run.py | 1 + + 7 files changed, 401 insertions(+), 93 deletions(-) + create mode 100644 src/csma/examples/csma-duplex.cc diff --git a/src/csma/doc/csma.rst b/src/csma/doc/csma.rst -index 13cc3c876..e2c444ad5 100644 +index 13cc3c876..c4f59ce47 100644 --- a/src/csma/doc/csma.rst +++ b/src/csma/doc/csma.rst @@ -14,6 +14,12 @@ The |ns3| CSMA device models a simple bus network in the spirit of Ethernet. @@ -16,7 +26,7 @@ index 13cc3c876..e2c444ad5 100644 +communication restricts a node to either transmit or receive at a time but not +perform both actions concurrently. By default, the |ns3| CSMA device models a +half-duplex bus network, with no fixed limit on the number of devices connected -+to the shared transmission medium. ++to the shared transmission medium. + Typically when one thinks of a bus network Ethernet or IEEE 802.3 comes to mind. Ethernet uses CSMA/CD (Carrier Sense Multiple Access with Collision Detection @@ -136,10 +146,10 @@ index 6bf85da2a..201fa9d3d 100644 example diff --git a/src/csma/examples/csma-duplex.cc b/src/csma/examples/csma-duplex.cc new file mode 100644 -index 000000000..7d65f1096 +index 000000000..c9a3a3f2b --- /dev/null +++ b/src/csma/examples/csma-duplex.cc -@@ -0,0 +1,103 @@ +@@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017 Luciano Jerez Chaves + * @@ -219,13 +229,15 @@ index 000000000..7d65f1096 + + // TCP traffic from host 0 to 1 + BulkSendHelper sender0("ns3::TcpSocketFactory", InetSocketAddress(h1Addr, 10000)); -+ PacketSinkHelper sink1("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), 10000)); ++ PacketSinkHelper sink1("ns3::TcpSocketFactory", ++ InetSocketAddress(Ipv4Address::GetAny(), 10000)); + senderApps.Add(sender0.Install(hosts.Get(0))); + sinkApps.Add(sink1.Install(hosts.Get(1))); + + // TCP traffic from host 1 to 0 + BulkSendHelper sender1("ns3::TcpSocketFactory", InetSocketAddress(h0Addr, 10001)); -+ PacketSinkHelper sink0("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), 10001)); ++ PacketSinkHelper sink0("ns3::TcpSocketFactory", ++ InetSocketAddress(Ipv4Address::GetAny(), 10001)); + senderApps.Add(sender1.Install(hosts.Get(1))); + sinkApps.Add(sink0.Install(hosts.Get(0))); + @@ -244,7 +256,7 @@ index 000000000..7d65f1096 + std::cout << "Total bytes sent from H1 to H0: " << pktSink0->GetTotalRx() << std::endl; +} diff --git a/src/csma/model/csma-channel.cc b/src/csma/model/csma-channel.cc -index 9f1fad3ce..fca9721cf 100644 +index 9f1fad3ce..2563bd662 100644 --- a/src/csma/model/csma-channel.cc +++ b/src/csma/model/csma-channel.cc @@ -1,5 +1,8 @@ @@ -457,13 +469,11 @@ index 9f1fad3ce..fca9721cf 100644 { return false; } -@@ -332,10 +377,10 @@ CsmaChannel::GetDelay() - return m_delay; +@@ -333,9 +378,9 @@ CsmaChannel::GetDelay() } --WireState + WireState -CsmaChannel::GetState() -+CsmaChannel::WireState +CsmaChannel::GetState(uint32_t deviceId) { - return m_state; @@ -500,7 +510,7 @@ index 9f1fad3ce..fca9721cf 100644 +} + +void -+CsmaChannel::SetState(uint32_t deviceId, CsmaChannel::WireState state) ++CsmaChannel::SetState(uint32_t deviceId, WireState state) +{ + m_state[m_fullDuplex ? deviceId : 0] = state; +} @@ -509,7 +519,7 @@ index 9f1fad3ce..fca9721cf 100644 { active = false; diff --git a/src/csma/model/csma-channel.h b/src/csma/model/csma-channel.h -index da4e948da..e3654cf59 100644 +index da4e948da..5d824f073 100644 --- a/src/csma/model/csma-channel.h +++ b/src/csma/model/csma-channel.h @@ -1,5 +1,8 @@ @@ -537,24 +547,7 @@ index da4e948da..e3654cf59 100644 #include "ns3/channel.h" #include "ns3/data-rate.h" #include "ns3/nstime.h" -@@ -68,16 +75,6 @@ class CsmaDeviceRec - bool IsActive() const; - }; - --/** -- * Current state of the channel -- */ --enum WireState --{ -- IDLE, /**< Channel is IDLE, no packet is being transmitted */ -- TRANSMITTING, /**< Channel is BUSY, a packet is being written by a net device */ -- PROPAGATING /**< Channel is BUSY, packet is propagating to all attached net devices */ --}; -- - /** - * \ingroup csma - * \brief Csma Channel. -@@ -86,11 +83,25 @@ enum WireState +@@ -86,7 +93,11 @@ enum WireState * when many nodes are connected to one wire. It uses a single busy * flag to indicate if the channel is currently in use. It does not * take into account the distances between stations or the speed of @@ -567,20 +560,6 @@ index da4e948da..e3654cf59 100644 */ class CsmaChannel : public Channel { - public: -+ /** -+ * Current state of the channel -+ */ -+ enum WireState -+ { -+ IDLE, /**< Channel is IDLE, no packet is being transmitted */ -+ TRANSMITTING, /**< Channel is BUSY, a packet is being written by a net device */ -+ PROPAGATING /**< Channel is BUSY, packet is propagating to all attached net devices */ -+ }; -+ - /** - * \brief Get the type ID. - * \return the object TypeId @@ -201,19 +212,23 @@ class CsmaChannel : public Channel * packet p as the m_currentPkt, the packet being currently * transmitting. @@ -747,7 +726,7 @@ index da4e948da..e3654cf59 100644 } // namespace ns3 diff --git a/src/csma/model/csma-net-device.cc b/src/csma/model/csma-net-device.cc -index e10648f96..647ea50c3 100644 +index e10648f96..c164c1d13 100644 --- a/src/csma/model/csma-net-device.cc +++ b/src/csma/model/csma-net-device.cc @@ -1,5 +1,8 @@ @@ -787,7 +766,7 @@ index e10648f96..647ea50c3 100644 + // Otherwise, we have to wait. // - if (m_channel->GetState() != IDLE) -+ if (m_channel->GetState(m_deviceId) != CsmaChannel::IDLE) ++ if (m_channel->GetState(m_deviceId) != IDLE) { // - // The channel is busy -- backoff and rechedule TransmitStart() unless @@ -844,7 +823,7 @@ index e10648f96..647ea50c3 100644 NS_ASSERT_MSG(m_txMachineState == BUSY, "CsmaNetDevice::transmitCompleteEvent(): Must be BUSY if transmitting"); - NS_ASSERT(m_channel->GetState() == TRANSMITTING); -+ NS_ASSERT(m_channel->GetState(m_deviceId) == CsmaChannel::TRANSMITTING); ++ NS_ASSERT(m_channel->GetState(m_deviceId) == TRANSMITTING); m_txMachineState = GAP; // @@ -924,3 +903,6 @@ index eba7b5b77..b27be27b1 100644 ("csma-multicast", "True", "True"), ("csma-one-subnet", "True", "True"), ("csma-packet-socket", "True", "True"), +-- +2.34.1 + From 2acf05426bc6d803ea2b4a92bf4d4126bc09289f Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 10:05:26 -0300 Subject: [PATCH 07/10] Patches for ns-3.39 --- utils/csma-full-duplex-3_39.patch | 908 ++++++++++++++++++++++++++++++ utils/ofswitch13-3_39.patch | 189 +++++++ 2 files changed, 1097 insertions(+) create mode 100644 utils/csma-full-duplex-3_39.patch create mode 100644 utils/ofswitch13-3_39.patch diff --git a/utils/csma-full-duplex-3_39.patch b/utils/csma-full-duplex-3_39.patch new file mode 100644 index 0000000..4d633be --- /dev/null +++ b/utils/csma-full-duplex-3_39.patch @@ -0,0 +1,908 @@ +From 176c16929aa15119f1d5a6b90b5fdfdf7406368d Mon Sep 17 00:00:00 2001 +From: Luciano J Chaves +Date: Fri, 1 Sep 2023 10:00:47 -0300 +Subject: [PATCH] CSMA full-duplex patch + +--- + src/csma/doc/csma.rst | 62 +++++++++----- + src/csma/examples/CMakeLists.txt | 1 + + src/csma/examples/csma-duplex.cc | 105 +++++++++++++++++++++++ + src/csma/model/csma-channel.cc | 133 +++++++++++++++++++++++------- + src/csma/model/csma-channel.h | 112 ++++++++++++++++++++++--- + src/csma/model/csma-net-device.cc | 80 +++++++++++------- + src/csma/test/examples-to-run.py | 1 + + 7 files changed, 401 insertions(+), 93 deletions(-) + create mode 100644 src/csma/examples/csma-duplex.cc + +diff --git a/src/csma/doc/csma.rst b/src/csma/doc/csma.rst +index 13cc3c876..c4f59ce47 100644 +--- a/src/csma/doc/csma.rst ++++ b/src/csma/doc/csma.rst +@@ -14,6 +14,12 @@ The |ns3| CSMA device models a simple bus network in the spirit of Ethernet. + Although it does not model any real physical network you could ever build or + buy, it does provide some very useful functionality. + ++When Ethernet was standardised, all communication was half-duplex. Half-duplex ++communication restricts a node to either transmit or receive at a time but not ++perform both actions concurrently. By default, the |ns3| CSMA device models a ++half-duplex bus network, with no fixed limit on the number of devices connected ++to the shared transmission medium. ++ + Typically when one thinks of a bus network Ethernet or IEEE 802.3 comes to mind. + Ethernet uses CSMA/CD (Carrier Sense Multiple Access with Collision Detection + with exponentially increasing backoff to contend for the shared transmission +@@ -23,6 +29,12 @@ light) carrier sense and priority-based collision "avoidance." Collisions in the + sense of Ethernet never happen and so the |ns3| CSMA device does not model + collision detection, nor will any transmission in progress be "jammed." + ++This model also offers support for full-duplex communication, where the ++transmission medium can operate in both directions simultaneously but is ++restricted to a maximum of two devices connected on the single full-duplex ++link. When in full-duplex operation, the original CSMA/CD protocol is shut off ++and the two devices on the link can send data independently. ++ + CSMA Layer Model + ++++++++++++++++ + +@@ -83,8 +95,7 @@ the protocol stack. + CSMA Channel Model + ****************** + +-The class CsmaChannel models the actual transmission medium. There is no fixed +-limit for the number of devices connected to the channel. The CsmaChannel models ++The class CsmaChannel models the actual transmission medium. The CsmaChannel models + a data rate and a speed-of-light delay which can be accessed via the attributes + "DataRate" and "Delay" respectively. The data rate provided to the channel is + used to set the data rates used by the transmitter sections of the CSMA devices +@@ -94,6 +105,18 @@ limitation (other than by the data type holding the value) on the speed at which + CSMA channels and devices can operate; and no restriction based on any kind of + PHY characteristics. + ++The CsmaChannel can operate in half-duplex or in full-duplex mode, which can be ++defined via the attribute "FullDuplex". The CsmaChannel holds two internal ++subchannels. When operating in half-duplex mode, only the first subchannel is ++used by all devices for both transmission and reception procedures. In this ++case, the CsmaChannel models a broadcast medium so the packet is delivered to ++all of the devices on the channel (except the source) at the end of the ++propagation time. It is the responsibility of the sending device to determine ++whether or not it receives a packet broadcast over the channel. When the ++CsmaChannel is operating in full-duplex mode (with a maximum of two devices ++attached to the channel), each device uses its exclusive subchannel for ++unidirectional packet transmission, getting rid of the CSMA/CD mechanism. ++ + The CsmaChannel has three states, ``IDLE``, ``TRANSMITTING`` and + ``PROPAGATING``. These three states are "seen" instantaneously by all devices on + the channel. By this we mean that if one device begins or ends a simulated +@@ -133,15 +156,11 @@ through its cable to the hub; plus 2) the time it takes for the hub to forward + the packet out a port; plus 3) the time it takes for the signal in question to + propagate to the destination net device. + +-The CsmaChannel models a broadcast medium so the packet is delivered to all of +-the devices on the channel (including the source) at the end of the propagation +-time. It is the responsibility of the sending device to determine whether or not +-it receives a packet broadcast over the channel. +- + The CsmaChannel provides following Attributes: + + * DataRate: The bitrate for packet transmission on connected devices; +-* Delay: The speed of light transmission delay for the channel. ++* Delay: The speed of light transmission delay for the channel; ++* FullDuplex: Whether the channel is operating in full-duplex mode or not. + + CSMA Net Device Model + ********************* +@@ -195,7 +214,9 @@ The CsmaNetDevice implements a random exponential backoff algorithm that is + executed if the channel is determined to be busy (``TRANSMITTING`` or + ``PPROPAGATING``) when the device wants to start propagating. This results in a + random delay of up to pow (2, retries) - 1 microseconds before a retry is +-attempted. The default maximum number of retries is 1000. ++attempted. The default maximum number of retries is 1000. Note that the random ++exponential backoff algorithm is not used when the device is attached to a ++channel in full-duplex operation. + + Using the CsmaNetDevice + *********************** +@@ -349,16 +370,19 @@ net device. + Summary + ******* + +-The ns3 CSMA model is a simplistic model of an Ethernet-like network. It +-supports a Carrier-Sense function and allows for Multiple Access to a +-shared medium. It is not physical in the sense that the state of the +-medium is instantaneously shared among all devices. This means that there +-is no collision detection required in this model and none is implemented. +-There will never be a "jam" of a packet already on the medium. Access to +-the shared channel is on a first-come first-served basis as determined by +-the simulator scheduler. If the channel is determined to be busy by looking +-at the global state, a random exponential backoff is performed and a retry +-is attempted. ++The ns3 CSMA model is a simplistic model of an Ethernet-like network that can ++operate in half-duplex or in full-duplex modes. In half-duplex mode, it ++supports a Carrier-Sense function and allows for Multiple Access to a shared ++medium. It is not physical in the sense that the state of the medium is ++instantaneously shared among all devices. This means that there is no collision ++detection required in this model and none is implemented. There will never be a ++"jam" of a packet already on the medium. Access to the shared channel is on a ++first-come first-served basis as determined by the simulator scheduler. If the ++channel is determined to be busy by looking at the global state, a random ++exponential backoff is performed and a retry is attempted. When operating in ++full-duplex mode, restricted to a maximum of two devices on the channel, the ++transmission medium can operate in both directions simultaneously and there is ++no need of the original CSMA/CD protocol. + + Ns-3 Attributes provide a mechanism for setting various parameters in the + device and channel such as addresses, encapsulation modes and error model +diff --git a/src/csma/examples/CMakeLists.txt b/src/csma/examples/CMakeLists.txt +index 6bf85da2a..201fa9d3d 100644 +--- a/src/csma/examples/CMakeLists.txt ++++ b/src/csma/examples/CMakeLists.txt +@@ -3,6 +3,7 @@ set(base_examples + csma-broadcast + csma-packet-socket + csma-multicast ++ csma-duplex + ) + foreach( + example +diff --git a/src/csma/examples/csma-duplex.cc b/src/csma/examples/csma-duplex.cc +new file mode 100644 +index 000000000..c9a3a3f2b +--- /dev/null ++++ b/src/csma/examples/csma-duplex.cc +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (c) 2017 Luciano Jerez Chaves ++ * ++ * 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 ++ * ++ * Author: Luciano Jerez Chaves ++ */ ++ ++#include "ns3/applications-module.h" ++#include "ns3/core-module.h" ++#include "ns3/csma-module.h" ++#include "ns3/internet-module.h" ++#include "ns3/network-module.h" ++ ++using namespace ns3; ++ ++int ++main(int argc, char* argv[]) ++{ ++ bool verbose = false; ++ bool trace = false; ++ ++ CommandLine cmd; ++ cmd.AddValue("verbose", "Tell application to log if true", verbose); ++ cmd.AddValue("trace", "Tracing traffic to files", trace); ++ cmd.AddValue("duplex", "ns3::CsmaChannel::FullDuplex"); ++ cmd.Parse(argc, argv); ++ ++ if (verbose) ++ { ++ LogComponentEnable("CsmaNetDevice", LOG_LEVEL_ALL); ++ LogComponentEnable("CsmaChannel", LOG_LEVEL_ALL); ++ LogComponentEnable("Backoff", LOG_LEVEL_ALL); ++ } ++ ++ // Create the host nodes ++ NodeContainer hosts; ++ hosts.Create(2); ++ ++ // Connecting the hosts ++ CsmaHelper csmaHelper; ++ csmaHelper.SetChannelAttribute("DataRate", DataRateValue(DataRate("100Mbps"))); ++ csmaHelper.SetChannelAttribute("Delay", TimeValue(MilliSeconds(1))); ++ NetDeviceContainer hostDevices = csmaHelper.Install(hosts); ++ ++ // Enable pcap traces ++ if (trace) ++ { ++ csmaHelper.EnablePcap("csma-duplex", hostDevices); ++ } ++ ++ // Installing the tcp/ip stack into hosts ++ InternetStackHelper internet; ++ internet.Install(hosts); ++ ++ // Set IPv4 host address ++ Ipv4AddressHelper ipv4switches; ++ Ipv4InterfaceContainer internetIpIfaces; ++ ipv4switches.SetBase("10.1.1.0", "255.255.255.0"); ++ internetIpIfaces = ipv4switches.Assign(hostDevices); ++ Ipv4Address h0Addr = internetIpIfaces.GetAddress(0); ++ Ipv4Address h1Addr = internetIpIfaces.GetAddress(1); ++ ++ ApplicationContainer senderApps; ++ ApplicationContainer sinkApps; ++ ++ // TCP traffic from host 0 to 1 ++ BulkSendHelper sender0("ns3::TcpSocketFactory", InetSocketAddress(h1Addr, 10000)); ++ PacketSinkHelper sink1("ns3::TcpSocketFactory", ++ InetSocketAddress(Ipv4Address::GetAny(), 10000)); ++ senderApps.Add(sender0.Install(hosts.Get(0))); ++ sinkApps.Add(sink1.Install(hosts.Get(1))); ++ ++ // TCP traffic from host 1 to 0 ++ BulkSendHelper sender1("ns3::TcpSocketFactory", InetSocketAddress(h0Addr, 10001)); ++ PacketSinkHelper sink0("ns3::TcpSocketFactory", ++ InetSocketAddress(Ipv4Address::GetAny(), 10001)); ++ senderApps.Add(sender1.Install(hosts.Get(1))); ++ sinkApps.Add(sink0.Install(hosts.Get(0))); ++ ++ sinkApps.Start(Seconds(0)); ++ senderApps.Start(Seconds(0.01)); ++ senderApps.Stop(Seconds(5)); ++ ++ // Run the simulation ++ Simulator::Run(); ++ Simulator::Destroy(); ++ ++ // Transmitted bytes ++ Ptr pktSink0 = DynamicCast(sinkApps.Get(1)); ++ Ptr pktSink1 = DynamicCast(sinkApps.Get(0)); ++ std::cout << "Total bytes sent from H0 to H1: " << pktSink1->GetTotalRx() << std::endl; ++ std::cout << "Total bytes sent from H1 to H0: " << pktSink0->GetTotalRx() << std::endl; ++} +diff --git a/src/csma/model/csma-channel.cc b/src/csma/model/csma-channel.cc +index 9c9f253a6..e09138e45 100644 +--- a/src/csma/model/csma-channel.cc ++++ b/src/csma/model/csma-channel.cc +@@ -1,5 +1,8 @@ + /* + * Copyright (c) 2007 Emmanuelle Laprise ++ * Copyright (c) 2012 Jeff Young ++ * Copyright (c) 2014 Murphy McCauley ++ * Copyright (c) 2017 Luciano Jerez Chaves + * + * 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 +@@ -15,6 +18,9 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Emmanuelle Laprise ++ * Author: Jeff Young ++ * Author: Murphy McCauley ++ * Author: Luciano Jerez Chaves + */ + + #include "csma-channel.h" +@@ -50,7 +56,13 @@ CsmaChannel::GetTypeId() + "Transmission delay through the channel", + TimeValue(Seconds(0)), + MakeTimeAccessor(&CsmaChannel::m_delay), +- MakeTimeChecker()); ++ MakeTimeChecker()) ++ .AddAttribute("FullDuplex", ++ "Whether the channel is full-duplex mode.", ++ TypeId::ATTR_CONSTRUCT, ++ BooleanValue(false), ++ MakeBooleanAccessor(&CsmaChannel::m_fullDuplex), ++ MakeBooleanChecker()); + return tid; + } + +@@ -58,7 +70,6 @@ CsmaChannel::CsmaChannel() + : Channel() + { + NS_LOG_FUNCTION_NOARGS(); +- m_state = IDLE; + m_deviceList.clear(); + } + +@@ -74,9 +85,21 @@ CsmaChannel::Attach(Ptr device) + NS_LOG_FUNCTION(this << device); + NS_ASSERT(device); + ++ // ++ // For full-duplex links we can only attach two devices to the same channel ++ // since there is no backoff mechanism for concurrent transmissions. ++ // ++ if (m_fullDuplex && m_deviceList.size() >= 2) ++ { ++ NS_LOG_DEBUG("Falling back to half-duplex"); ++ m_fullDuplex = false; ++ } ++ + CsmaDeviceRec rec(device); + + m_deviceList.push_back(rec); ++ SetState(m_deviceList.size() - 1, IDLE); ++ SetCurrentSrc(m_deviceList.size() - 1, m_deviceList.size() - 1); + return (m_deviceList.size() - 1); + } + +@@ -110,7 +133,7 @@ CsmaChannel::Reattach(uint32_t deviceId) + { + NS_LOG_FUNCTION(this << deviceId); + +- if (deviceId < m_deviceList.size()) ++ if (deviceId >= m_deviceList.size()) + { + return false; + } +@@ -141,7 +164,7 @@ CsmaChannel::Detach(uint32_t deviceId) + + m_deviceList[deviceId].active = false; + +- if ((m_state == TRANSMITTING) && (m_currentSrc == deviceId)) ++ if ((GetState(deviceId) == TRANSMITTING) && (GetCurrentSrc(deviceId) == deviceId)) + { + NS_LOG_WARN("CsmaChannel::Detach(): Device is currently" + << "transmitting (" << deviceId << ")"); +@@ -179,7 +202,7 @@ CsmaChannel::TransmitStart(Ptr p, uint32_t srcId) + NS_LOG_FUNCTION(this << p << srcId); + NS_LOG_INFO("UID is " << p->GetUid() << ")"); + +- if (m_state != IDLE) ++ if (GetState(srcId) != IDLE) + { + NS_LOG_WARN("CsmaChannel::TransmitStart(): State is not IDLE"); + return false; +@@ -188,14 +211,14 @@ CsmaChannel::TransmitStart(Ptr p, uint32_t srcId) + if (!IsActive(srcId)) + { + NS_LOG_ERROR( +- "CsmaChannel::TransmitStart(): Seclected source is not currently attached to network"); ++ "CsmaChannel::TransmitStart(): Selected source is not currently attached to network"); + return false; + } + + NS_LOG_LOGIC("switch to TRANSMITTING"); +- m_currentPkt = p->Copy(); +- m_currentSrc = srcId; +- m_state = TRANSMITTING; ++ SetCurrentPkt(srcId, p->Copy()); ++ SetCurrentSrc(srcId, srcId); ++ SetState(srcId, TRANSMITTING); + return true; + } + +@@ -206,17 +229,23 @@ CsmaChannel::IsActive(uint32_t deviceId) + } + + bool +-CsmaChannel::TransmitEnd() ++CsmaChannel::IsFullDuplex() const ++{ ++ return m_fullDuplex; ++} ++ ++bool ++CsmaChannel::TransmitEnd(uint32_t srcId) + { +- NS_LOG_FUNCTION(this << m_currentPkt << m_currentSrc); +- NS_LOG_INFO("UID is " << m_currentPkt->GetUid() << ")"); ++ NS_LOG_FUNCTION(this << GetCurrentPkt(srcId) << GetCurrentSrc(srcId)); ++ NS_LOG_INFO("UID is " << GetCurrentPkt(srcId)->GetUid() << ")"); + +- NS_ASSERT(m_state == TRANSMITTING); +- m_state = PROPAGATING; ++ NS_ASSERT(GetState(srcId) == TRANSMITTING); ++ SetState(srcId, PROPAGATING); + + bool retVal = true; + +- if (!IsActive(m_currentSrc)) ++ if (!IsActive(GetCurrentSrc(srcId))) + { + NS_LOG_ERROR("CsmaChannel::TransmitEnd(): Seclected source was detached before the end of " + "the transmission"); +@@ -230,31 +259,47 @@ CsmaChannel::TransmitEnd() + std::vector::iterator it; + for (it = m_deviceList.begin(); it < m_deviceList.end(); it++) + { +- if (it->IsActive() && it->devicePtr != m_deviceList[m_currentSrc].devicePtr) ++ if (it->IsActive() && it->devicePtr != m_deviceList[GetCurrentSrc(srcId)].devicePtr) + { + // schedule reception events + Simulator::ScheduleWithContext(it->devicePtr->GetNode()->GetId(), + m_delay, + &CsmaNetDevice::Receive, + it->devicePtr, +- m_currentPkt->Copy(), +- m_deviceList[m_currentSrc].devicePtr); ++ GetCurrentPkt(srcId)->Copy(), ++ m_deviceList[GetCurrentSrc(srcId)].devicePtr); + } + } + +- // also schedule for the tx side to go back to IDLE +- Simulator::Schedule(m_delay, &CsmaChannel::PropagationCompleteEvent, this); ++ // Schedule for the TX side to go back to IDLE. ++ if (IsFullDuplex()) ++ { ++ // ++ // In full-duplex mode, the channel should be IDLE during propagation ++ // since it's ok to start transmitting again. In this case, we don't need ++ // to wait for the channel delay. ++ // ++ PropagationCompleteEvent(srcId); ++ } ++ else ++ { ++ // ++ // In half-duplex mode, the channel can only go back to IDLE after ++ // propagation delay. ++ // ++ Simulator::Schedule(m_delay, &CsmaChannel::PropagationCompleteEvent, this, srcId); ++ } + return retVal; + } + + void +-CsmaChannel::PropagationCompleteEvent() ++CsmaChannel::PropagationCompleteEvent(uint32_t deviceId) + { +- NS_LOG_FUNCTION(this << m_currentPkt); +- NS_LOG_INFO("UID is " << m_currentPkt->GetUid() << ")"); ++ NS_LOG_FUNCTION(this << GetCurrentPkt(deviceId)); ++ NS_LOG_INFO("UID is " << GetCurrentPkt(deviceId)->GetUid() << ")"); + +- NS_ASSERT(m_state == PROPAGATING); +- m_state = IDLE; ++ NS_ASSERT(GetState(deviceId) == PROPAGATING); ++ SetState(deviceId, IDLE); + } + + uint32_t +@@ -308,9 +353,9 @@ CsmaChannel::GetDeviceNum(Ptr device) + } + + bool +-CsmaChannel::IsBusy() ++CsmaChannel::IsBusy(uint32_t deviceId) + { +- return m_state != IDLE; ++ return GetState(deviceId) != IDLE; + } + + DataRate +@@ -326,9 +371,9 @@ CsmaChannel::GetDelay() + } + + WireState +-CsmaChannel::GetState() ++CsmaChannel::GetState(uint32_t deviceId) + { +- return m_state; ++ return m_state[m_fullDuplex ? deviceId : 0]; + } + + Ptr +@@ -337,6 +382,36 @@ CsmaChannel::GetDevice(std::size_t i) const + return GetCsmaDevice(i); + } + ++Ptr ++CsmaChannel::GetCurrentPkt(uint32_t deviceId) ++{ ++ return m_currentPkt[m_fullDuplex ? deviceId : 0]; ++} ++ ++void ++CsmaChannel::SetCurrentPkt(uint32_t deviceId, Ptr pkt) ++{ ++ m_currentPkt[m_fullDuplex ? deviceId : 0] = pkt; ++} ++ ++uint32_t ++CsmaChannel::GetCurrentSrc(uint32_t deviceId) ++{ ++ return m_currentSrc[m_fullDuplex ? deviceId : 0]; ++} ++ ++void ++CsmaChannel::SetCurrentSrc(uint32_t deviceId, uint32_t transmitterId) ++{ ++ m_currentSrc[m_fullDuplex ? deviceId : 0] = transmitterId; ++} ++ ++void ++CsmaChannel::SetState(uint32_t deviceId, WireState state) ++{ ++ m_state[m_fullDuplex ? deviceId : 0] = state; ++} ++ + CsmaDeviceRec::CsmaDeviceRec() + { + active = false; +diff --git a/src/csma/model/csma-channel.h b/src/csma/model/csma-channel.h +index da4e948da..5d824f073 100644 +--- a/src/csma/model/csma-channel.h ++++ b/src/csma/model/csma-channel.h +@@ -1,5 +1,8 @@ + /* + * Copyright (c) 2007 Emmanuelle Laprise ++ * Copyright (c) 2012 Jeffrey Young ++ * Copyright (c) 2014 Murphy McCauley ++ * Copyright (c) 2017 Luciano Jerez Chaves + * + * 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 +@@ -15,11 +18,15 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Emmanuelle Laprise ++ * Author: Jeff Young ++ * Author: Murphy McCauley ++ * Author: Luciano Jerez Chaves + */ + + #ifndef CSMA_CHANNEL_H + #define CSMA_CHANNEL_H + ++#include "ns3/boolean.h" + #include "ns3/channel.h" + #include "ns3/data-rate.h" + #include "ns3/nstime.h" +@@ -86,7 +93,11 @@ enum WireState + * when many nodes are connected to one wire. It uses a single busy + * flag to indicate if the channel is currently in use. It does not + * take into account the distances between stations or the speed of +- * light to determine collisions. ++ * light to determine collisions. Optionally, it allows for full- ++ * duplex operation when there are only two attached nodes. To ++ * implement full-duplex, we internally keep two TX "subchannels" ++ * (one for each attached node). When in half-duplex mode, only ++ * the first subchannel is used. + */ + class CsmaChannel : public Channel + { +@@ -201,19 +212,23 @@ class CsmaChannel : public Channel + * packet p as the m_currentPkt, the packet being currently + * transmitting. + * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. + * \return Returns true unless the source was detached before it + * completed its transmission. + */ +- bool TransmitEnd(); ++ bool TransmitEnd(uint32_t deviceId); + + /** + * \brief Indicates that the channel has finished propagating the + * current packet. The channel is released and becomes free. + * +- * Calls the receive function of every active net device that is ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * Calls the receive function of the other net device that is + * attached to the channel. + */ +- void PropagationCompleteEvent(); ++ void PropagationCompleteEvent(uint32_t deviceId); + + /** + * \return Returns the device number assigned to a net device by the +@@ -225,19 +240,25 @@ class CsmaChannel : public Channel + int32_t GetDeviceNum(Ptr device); + + /** ++ * \brief Checks the state of the channel. ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. + * \return Returns the state of the channel (IDLE -- free, + * TRANSMITTING -- busy, PROPAGATING - busy ) + */ +- WireState GetState(); ++ WireState GetState(uint32_t deviceId); + + /** + * \brief Indicates if the channel is busy. The channel will only + * accept new packets for transmission if it is not busy. + * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. + * \return Returns true if the channel is busy and false if it is + * free. + */ +- bool IsBusy(); ++ bool IsBusy(uint32_t deviceId); + + /** + * \brief Indicates if a net device is currently attached or +@@ -250,6 +271,14 @@ class CsmaChannel : public Channel + */ + bool IsActive(uint32_t deviceId); + ++ /** ++ * \brief Indicates if channel is operating in full-duplex mode. ++ * ++ * \return Returns true if channel is in full-duplex mode, false if in ++ * half-duplex mode. ++ */ ++ bool IsFullDuplex() const; ++ + /** + * \return Returns the number of net devices that are currently + * attached to the channel. +@@ -307,6 +336,11 @@ class CsmaChannel : public Channel + */ + Time m_delay; + ++ /** ++ * Whether the channel is in full-duplex mode. ++ */ ++ bool m_fullDuplex; ++ + /** + * List of the net devices that have been or are currently connected + * to the channel. +@@ -321,23 +355,75 @@ class CsmaChannel : public Channel + std::vector m_deviceList; + + /** +- * The Packet that is currently being transmitted on the channel (or last +- * packet to have been transmitted on the channel if the channel is +- * free.) ++ * The Packet that is currently being transmitted on the subchannel (or the ++ * last packet to have been transmitted if the subchannel is free). ++ * In half-duplex mode, only the first subchannel is used. + */ +- Ptr m_currentPkt; ++ Ptr m_currentPkt[2]; + + /** + * Device Id of the source that is currently transmitting on the ++ * subchannel, or the last source to have transmitted a packet on the ++ * subchannel, if it is not currently busy. ++ * In half-duplex mode, only the first subchannel is used. ++ */ ++ uint32_t m_currentSrc[2]; ++ ++ /** ++ * Current state of each subchannel. ++ * In half-duplex mode, only the first subchannel is used. ++ */ ++ WireState m_state[2]; ++ ++ /** ++ * \brief Gets current packet ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * \return Device Id of the source that is currently transmitting on the ++ * subchannel or the last source to have transmitted a packet on the ++ * subchannel, if it is not currently busy. ++ */ ++ Ptr GetCurrentPkt(uint32_t deviceId); ++ ++ /** ++ * \brief Sets the current packet ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * \param The Packet that is current being transmitted by deviceId (or last ++ * packet to have been transmitted on the channel if the channel is free.) ++ */ ++ void SetCurrentPkt(uint32_t deviceId, Ptr pkt); ++ ++ /** ++ * \brief Gets current transmitter ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * \return Device Id of the source that is currently transmitting on the + * channel. Or last source to have transmitted a packet on the + * channel, if the channel is currently not busy. + */ +- uint32_t m_currentSrc; ++ uint32_t GetCurrentSrc(uint32_t deviceId); + + /** +- * Current state of the channel ++ * \brief Sets the current transmitter ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * \param transmitterId The ID of the transmitting device. ++ */ ++ void SetCurrentSrc(uint32_t deviceId, uint32_t transmitterId); ++ ++ /** ++ * \brief Sets the state of the channel ++ * ++ * \param deviceId The ID that was assigned to the net device when ++ * it was attached to the channel. ++ * \param state The new channel state. + */ +- WireState m_state; ++ void SetState(uint32_t deviceId, WireState state); + }; + + } // namespace ns3 +diff --git a/src/csma/model/csma-net-device.cc b/src/csma/model/csma-net-device.cc +index f6d3ff4ae..e2998cd19 100644 +--- a/src/csma/model/csma-net-device.cc ++++ b/src/csma/model/csma-net-device.cc +@@ -1,5 +1,8 @@ + /* + * Copyright (c) 2007 Emmanuelle Laprise ++ * Copyright (c) 2012 Jeffrey Young ++ * Copyright (c) 2014 Murphy McCauley ++ * Copyright (c) 2017 Luciano Jerez Chaves + * + * 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 +@@ -15,6 +18,9 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Emmanuelle Laprise ++ * Author: Jeff Young ++ * Author: Murphy McCauley ++ * Author: Luciano Jerez Chaves + */ + + #include "csma-net-device.h" +@@ -455,6 +461,7 @@ CsmaNetDevice::TransmitStart() + + NS_LOG_LOGIC("m_currentPkt = " << m_currentPkt); + NS_LOG_LOGIC("UID = " << m_currentPkt->GetUid()); ++ NS_LOG_LOGIC("Device ID = " << m_deviceId); + + // + // Only transmit if the send side of net device is enabled +@@ -476,34 +483,38 @@ CsmaNetDevice::TransmitStart() + "Must be READY to transmit. Tx state is: " << m_txMachineState); + + // +- // Now we have to sense the state of the medium and either start transmitting +- // if it is idle, or backoff our transmission if someone else is on the wire. ++ // Now we sense the state of the medium. If idle, we start transmitting. ++ // Otherwise, we have to wait. + // +- if (m_channel->GetState() != IDLE) ++ if (m_channel->GetState(m_deviceId) != IDLE) + { + // +- // The channel is busy -- backoff and rechedule TransmitStart() unless +- // we have exhausted all of our retries. ++ // The (sub)channel is busy. If in half-duplex mode, backoff and ++ // reschedule TransmitStart() unless we have exhausted all of our ++ // retries. This is not supposed to happen in full-duplex mode. + // +- m_txMachineState = BACKOFF; +- +- if (m_backoff.MaxRetriesReached()) +- { +- // +- // Too many retries, abort transmission of packet +- // +- TransmitAbort(); +- } +- else ++ if (m_channel->IsFullDuplex() == false) + { +- m_macTxBackoffTrace(m_currentPkt); +- +- m_backoff.IncrNumRetries(); +- Time backoffTime = m_backoff.GetBackoffTime(); +- +- NS_LOG_LOGIC("Channel busy, backing off for " << backoffTime.As(Time::S)); +- +- Simulator::Schedule(backoffTime, &CsmaNetDevice::TransmitStart, this); ++ m_txMachineState = BACKOFF; ++ ++ if (m_backoff.MaxRetriesReached()) ++ { ++ // ++ // Too many retries, abort transmission of packet ++ // ++ TransmitAbort(); ++ } ++ else ++ { ++ m_macTxBackoffTrace(m_currentPkt); ++ ++ m_backoff.IncrNumRetries(); ++ Time backoffTime = m_backoff.GetBackoffTime(); ++ ++ NS_LOG_LOGIC("Channel busy, backing off for " << backoffTime.As(Time::S)); ++ ++ Simulator::Schedule(backoffTime, &CsmaNetDevice::TransmitStart, this); ++ } + } + } + else +@@ -595,7 +606,7 @@ CsmaNetDevice::TransmitCompleteEvent() + // + NS_ASSERT_MSG(m_txMachineState == BUSY, + "CsmaNetDevice::transmitCompleteEvent(): Must be BUSY if transmitting"); +- NS_ASSERT(m_channel->GetState() == TRANSMITTING); ++ NS_ASSERT(m_channel->GetState(m_deviceId) == TRANSMITTING); + m_txMachineState = GAP; + + // +@@ -605,8 +616,9 @@ CsmaNetDevice::TransmitCompleteEvent() + NS_ASSERT_MSG(m_currentPkt, "CsmaNetDevice::TransmitCompleteEvent(): m_currentPkt zero"); + NS_LOG_LOGIC("m_currentPkt=" << m_currentPkt); + NS_LOG_LOGIC("Pkt UID is " << m_currentPkt->GetUid() << ")"); ++ NS_LOG_LOGIC("Device ID is " << m_deviceId); + +- m_channel->TransmitEnd(); ++ m_channel->TransmitEnd(m_deviceId); + m_phyTxEndTrace(m_currentPkt); + m_currentPkt = nullptr; + +@@ -663,6 +675,8 @@ CsmaNetDevice::Attach(Ptr ch) + + m_deviceId = m_channel->Attach(this); + ++ NS_LOG_FUNCTION("Device ID is " << m_deviceId); ++ + // + // The channel provides us with the transmitter data rate. + // +@@ -699,6 +713,7 @@ CsmaNetDevice::Receive(Ptr packet, Ptr senderDevice) + { + NS_LOG_FUNCTION(packet << senderDevice); + NS_LOG_LOGIC("UID is " << packet->GetUid()); ++ NS_LOG_LOGIC("Device ID is " << m_deviceId); + + // + // We never forward up packets that we sent. Real devices don't do this since +@@ -996,6 +1011,7 @@ CsmaNetDevice::SendFrom(Ptr packet, + NS_LOG_FUNCTION(packet << src << dest << protocolNumber); + NS_LOG_LOGIC("packet =" << packet); + NS_LOG_LOGIC("UID is " << packet->GetUid() << ")"); ++ NS_LOG_LOGIC("Device ID is " << m_deviceId); + + NS_ASSERT(IsLinkUp()); + +@@ -1052,13 +1068,6 @@ CsmaNetDevice::GetNode() const + return m_node; + } + +-void +-CsmaNetDevice::SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb) +-{ +- NS_LOG_FUNCTION(&cb); +- m_openFlowRxCallback = cb; +-} +- + void + CsmaNetDevice::SetNode(Ptr node) + { +@@ -1074,6 +1083,13 @@ CsmaNetDevice::NeedsArp() const + return true; + } + ++void ++CsmaNetDevice::SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb) ++{ ++ NS_LOG_FUNCTION(&cb); ++ m_openFlowRxCallback = cb; ++} ++ + void + CsmaNetDevice::SetReceiveCallback(NetDevice::ReceiveCallback cb) + { +diff --git a/src/csma/test/examples-to-run.py b/src/csma/test/examples-to-run.py +index 3e49f1972..c671e5427 100644 +--- a/src/csma/test/examples-to-run.py ++++ b/src/csma/test/examples-to-run.py +@@ -8,6 +8,7 @@ + # See test.py for more information. + cpp_examples = [ + ("csma-broadcast", "True", "True"), ++ ("csma-duplex", "True", "True"), + ("csma-multicast", "True", "True"), + ("csma-one-subnet", "True", "True"), + ("csma-packet-socket", "True", "True"), +-- +2.34.1 + diff --git a/utils/ofswitch13-3_39.patch b/utils/ofswitch13-3_39.patch new file mode 100644 index 0000000..7f72cfc --- /dev/null +++ b/utils/ofswitch13-3_39.patch @@ -0,0 +1,189 @@ +From b89fa2043c3af1edd2270eb375756d6525bff331 Mon Sep 17 00:00:00 2001 +From: Luciano J Chaves +Date: Fri, 1 Sep 2023 09:53:24 -0300 +Subject: [PATCH] OFSwitch13 patch + +--- + .../custom-modules/ns3-configtable.cmake | 3 ++ + src/csma/model/csma-net-device.cc | 35 +++++++++++++++++++ + src/csma/model/csma-net-device.h | 13 +++++++ + .../model/virtual-net-device.cc | 30 ++++++++++++++++ + .../model/virtual-net-device.h | 11 ++++++ + 5 files changed, 92 insertions(+) + +diff --git a/build-support/custom-modules/ns3-configtable.cmake b/build-support/custom-modules/ns3-configtable.cmake +index c3baa514b..3b7434750 100644 +--- a/build-support/custom-modules/ns3-configtable.cmake ++++ b/build-support/custom-modules/ns3-configtable.cmake +@@ -161,6 +161,9 @@ macro(write_configtable) + string(APPEND out "ns-3 Click Integration : ") + check_on_or_off("ON" "${NS3_CLICK}") + ++ string(APPEND out "ns-3 OFSwitch13 Integration : ") ++ check_on_or_off("ON" "${NS3_OFSWITCH13}") ++ + string(APPEND out "ns-3 OpenFlow Integration : ") + check_on_or_off("ON" "${NS3_OPENFLOW}") + +diff --git a/src/csma/model/csma-net-device.cc b/src/csma/model/csma-net-device.cc +index bb0b93894..f6d3ff4ae 100644 +--- a/src/csma/model/csma-net-device.cc ++++ b/src/csma/model/csma-net-device.cc +@@ -806,6 +806,34 @@ CsmaNetDevice::Receive(Ptr packet, Ptr senderDevice) + packetType = PACKET_OTHERHOST; + } + ++ // ++ // Check if this device is configure as an OpenFlow switch port. ++ // ++ if (!m_openFlowRxCallback.IsNull()) ++ { ++ // For all kinds of packet we receive, we hit the promiscuous sniffer ++ // hook. If the packet is addressed to this device (which is not supposed ++ // to happen in normal situations), we also hit the non-promiscuous ++ // sniffer hook, but in both cases we don't forward the packet up the ++ // stack. ++ m_promiscSnifferTrace(originalPacket); ++ if (packetType != PACKET_OTHERHOST) ++ { ++ m_snifferTrace(originalPacket); ++ } ++ ++ // We forward the original packet (which includes the EthernetHeader) to ++ // the OpenFlow receive callback for all kinds of packetType we receive ++ // (broadcast, multicast, host or other host). ++ m_openFlowRxCallback(this, ++ originalPacket, ++ protocol, ++ header.GetSource(), ++ header.GetDestination(), ++ packetType); ++ return; ++ } ++ + // + // For all kinds of packetType we receive, we hit the promiscuous sniffer + // hook and pass a copy up to the promiscuous callback. Pass a copy to +@@ -1024,6 +1052,13 @@ CsmaNetDevice::GetNode() const + return m_node; + } + ++void ++CsmaNetDevice::SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb) ++{ ++ NS_LOG_FUNCTION(&cb); ++ m_openFlowRxCallback = cb; ++} ++ + void + CsmaNetDevice::SetNode(Ptr node) + { +diff --git a/src/csma/model/csma-net-device.h b/src/csma/model/csma-net-device.h +index 237daf37e..226ea7477 100644 +--- a/src/csma/model/csma-net-device.h ++++ b/src/csma/model/csma-net-device.h +@@ -312,6 +312,14 @@ class CsmaNetDevice : public NetDevice + */ + bool NeedsArp() const override; + ++ /** ++ * Set the callback used to notify the OpenFlow when a packet has been ++ * received by this device. ++ * ++ * \param cb The callback. ++ */ ++ void SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb); ++ + /** + * Set the callback to be used to notify higher layers when a packet has been + * received. +@@ -692,6 +700,11 @@ class CsmaNetDevice : public NetDevice + */ + Mac48Address m_address; + ++ /** ++ * The OpenFlow receive callback. ++ */ ++ NetDevice::PromiscReceiveCallback m_openFlowRxCallback; ++ + /** + * The callback used to notify higher layers that a packet has been received. + */ +diff --git a/src/virtual-net-device/model/virtual-net-device.cc b/src/virtual-net-device/model/virtual-net-device.cc +index 31518b7dc..e7613508f 100644 +--- a/src/virtual-net-device/model/virtual-net-device.cc ++++ b/src/virtual-net-device/model/virtual-net-device.cc +@@ -142,6 +142,29 @@ VirtualNetDevice::Receive(Ptr packet, + const Address& destination, + PacketType packetType) + { ++ // ++ // Check if this device is configure as an OpenFlow switch port. ++ // ++ if (!m_openFlowRxCallback.IsNull()) ++ { ++ // For all kinds of packetType we receive, we hit the promiscuous sniffer ++ // hook. If the packet is addressed to this device (which is not supposed ++ // to happen in normal situations), we also hit the non-promiscuous ++ // sniffer hook, but in both cases we don't forward the packet up the ++ // stack. ++ m_promiscSnifferTrace(packet); ++ if (packetType != PACKET_OTHERHOST) ++ { ++ m_snifferTrace(packet); ++ } ++ ++ // We then forward the original packet to the OpenFlow receive callback ++ // for all kinds of packetType we receive (broadcast, multicast, host or ++ // other host). ++ m_openFlowRxCallback(this, packet, protocol, source, destination, packetType); ++ return true; ++ } ++ + // + // For all kinds of packetType we receive, we hit the promiscuous sniffer + // hook and pass a copy up to the promiscuous callback. Pass a copy to +@@ -311,4 +334,11 @@ VirtualNetDevice::IsBridge() const + return false; + } + ++void ++VirtualNetDevice::SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb) ++{ ++ NS_LOG_FUNCTION(&cb); ++ m_openFlowRxCallback = cb; ++} ++ + } // namespace ns3 +diff --git a/src/virtual-net-device/model/virtual-net-device.h b/src/virtual-net-device/model/virtual-net-device.h +index 66718596a..c0eb0c468 100644 +--- a/src/virtual-net-device/model/virtual-net-device.h ++++ b/src/virtual-net-device/model/virtual-net-device.h +@@ -153,6 +153,14 @@ class VirtualNetDevice : public NetDevice + bool SupportsSendFrom() const override; + bool IsBridge() const override; + ++ /** ++ * Set the callback used to notify the OpenFlow when a packet has been ++ * received by this device. ++ * ++ * \param cb The OpenFlow receive callback. ++ */ ++ void SetOpenFlowReceiveCallback(NetDevice::PromiscReceiveCallback cb); ++ + protected: + void DoDispose() override; + +@@ -173,6 +181,9 @@ class VirtualNetDevice : public NetDevice + bool m_needsArp; //!< True if the device needs ARP + bool m_supportsSendFrom; //!< True if the device supports SendFrm + bool m_isPointToPoint; //!< True if the device is a PointToPoint type device ++ ++ /** The OpenFlow receive callback. */ ++ NetDevice::PromiscReceiveCallback m_openFlowRxCallback; + }; + + } // namespace ns3 +-- +2.34.1 + From 13460771540f9b8319a352e0e6c93dc3869af254 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 10:05:42 -0300 Subject: [PATCH 08/10] Updating patch for ns-3.38 --- utils/ofswitch13-3_38.patch | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/utils/ofswitch13-3_38.patch b/utils/ofswitch13-3_38.patch index 316f97a..0865000 100644 --- a/utils/ofswitch13-3_38.patch +++ b/utils/ofswitch13-3_38.patch @@ -1,3 +1,16 @@ +From 598058c4f0d47d8635b1b57d62068195cba7f006 Mon Sep 17 00:00:00 2001 +From: Luciano J Chaves +Date: Fri, 1 Sep 2023 10:03:56 -0300 +Subject: [PATCH] OFSwitch13 patch + +--- + .../custom-modules/ns3-configtable.cmake | 3 ++ + src/csma/model/csma-net-device.cc | 35 +++++++++++++++++++ + src/csma/model/csma-net-device.h | 13 +++++++ + .../model/virtual-net-device.cc | 30 ++++++++++++++++ + .../model/virtual-net-device.h | 11 ++++++ + 5 files changed, 92 insertions(+) + diff --git a/build-support/custom-modules/ns3-configtable.cmake b/build-support/custom-modules/ns3-configtable.cmake index c3baa514b..3b7434750 100644 --- a/build-support/custom-modules/ns3-configtable.cmake @@ -171,3 +184,6 @@ index 66718596a..c0eb0c468 100644 }; } // namespace ns3 +-- +2.34.1 + From 7d4501f952b85094b87d98cff8a4185853645fe3 Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 10:09:19 -0300 Subject: [PATCH 09/10] Updating release notes. --- RELEASE_NOTES | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index badbfdb..3580363 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -22,9 +22,8 @@ Release 5.2.1 (Mar 31, 2023) - Replacing V4PingHelper by PingHelper in examples for ns-3.38 compatibility. -- This version is compatible with BOFUSS library release v5.2.x and ns-3 - version 3.38 (including patches). There is no backward compatibility with - older ns-3 versions. +- This version is compatible with ns-3 version 3.38 (including patches). There + is no backward compatibility with older ns-3 versions. - Compilation was tested in Ubuntu 22.04.1 LTS. @@ -42,9 +41,8 @@ Release 5.2.0 (Mar 31, 2023) - Refactoring the BOFUSS ns3lib branch to simplify library compilation. -- This version is compatible with BOFUSS library release v5.2.x and ns-3 - versions 3.36 and 3.37 (including patches). There is no backward - compatibility with older ns-3 versions. +- This version is compatible with ns-3 versions 3.36 and 3.37 (including + patches). There is no backward compatibility with older ns-3 versions. - Compilation was tested in Ubuntu 22.04.1 LTS. From ee112bb04197fba42da7034ef90b2fd00444bcec Mon Sep 17 00:00:00 2001 From: Luciano J Chaves Date: Fri, 1 Sep 2023 10:37:57 -0300 Subject: [PATCH 10/10] Updating release number. --- RELEASE_NOTES | 8 ++++++-- doc/source/conf.py | 4 ++-- doc/source/ofswitch13-description.rst | 4 ++-- doc/source/ofswitch13-usage.rst | 10 +++++----- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3580363..ecc814f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -4,8 +4,7 @@ OFSwitch13 release notes This file contains OFSwitch13 release notes (most recent releases first). - -Release X.X.X (XXX XX, XXXX) +Release 5.2.2 (Sep 01, 2023) ============================ - Removing examples/ofswitch13-qos-controller dependency from netanim module. @@ -16,6 +15,11 @@ Release X.X.X (XXX XX, XXXX) maximum size of internal queues. The operation mode (packets or bytes) of all internal queues must be the same. +- This version is compatible with ns-3 versions 3.38 and 3.39 (including + patches). There is no backward compatibility with older ns-3 versions. + +- Compilation was tested in Ubuntu 22.04.1 LTS. + Release 5.2.1 (Mar 31, 2023) ============================ diff --git a/doc/source/conf.py b/doc/source/conf.py index bce8441..1b5a32b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -73,9 +73,9 @@ # built documents. # # The short X.Y version. -version = u'5.2.1' +version = u'5.2.2' # The full version, including alpha/beta/rc tags. -release = u'5.2.1' +release = u'5.2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/ofswitch13-description.rst b/doc/source/ofswitch13-description.rst index 38c6cbd..1c6bfe9 100644 --- a/doc/source/ofswitch13-description.rst +++ b/doc/source/ofswitch13-description.rst @@ -204,8 +204,8 @@ For |ns3| OpenFlow users who want to port existing code to this new module, plea The only required modification to the |ns3| source code for |ofs13| integration is the inclusion of the new OpenFlow receive callback in the ``CsmaNetDevice`` and ``VirtualNetDevice``. The module brings the patch for including this receive callback into |ns3| source code, available under ``utils/`` directory. -The current |ofs13| stable version is 5.2.1. -This version is compatible with |ns3| version 3.38, and will not compile with older |ns3| versions. +The current |ofs13| stable version is 5.2.2. +This version is compatible with |ns3| versions 3.38 and 3.39, and will not compile with older |ns3| versions. If you need to use another |ns3| release, you can check the RELEASE_NOTES file for previous |ofs13| releases and their |ns3| version compatibility, but keep in mind that old releases may have known bugs and an old API. It is strongly recommended to use the latest module version. diff --git a/doc/source/ofswitch13-usage.rst b/doc/source/ofswitch13-usage.rst index 4029723..a88b21b 100644 --- a/doc/source/ofswitch13-usage.rst +++ b/doc/source/ofswitch13-usage.rst @@ -32,13 +32,13 @@ Before starting, ensure you have the following minimal requirements installed on Compiling the code ################## -Clone the |ns3| source code repository into your machine and checkout a stable version (we are using the ns-3.38): +Clone the |ns3| source code repository into your machine and checkout a stable version (we are using the ns-3.39): .. code-block:: bash $ git clone https://gitlab.com/nsnam/ns-3-dev.git $ cd ns-3-dev - $ git checkout -b ns-3.38 ns-3.38 + $ git checkout -b ns-3.39 ns-3.39 Download the |ofs13| code into the ``contrib/`` folder. @@ -47,7 +47,7 @@ Download the |ofs13| code into the ``contrib/`` folder. $ cd contrib/ $ git clone https://github.com/ljerezchaves/ofswitch13.git -Update the |ofs13| code to a stable version (we are using release 5.2.1, which is compatible with ns-3.38) [#f2]_: +Update the |ofs13| code to a stable version (we are using release 5.2.2, which is compatible with ns-3.39) [#f2]_: .. [#f2] Starting at |ofs13| release 5.2.0, the ``cmake`` build system will automatically download and compile the correct version of |bofuss| library (Internet connection is required). For older |ofs13| releases, we suggest you check the documentation and follow the proper build steps. @@ -55,14 +55,14 @@ Update the |ofs13| code to a stable version (we are using release 5.2.1, which i .. code-block:: bash $ cd ofswitch13 - $ git checkout 5.2.1 + $ git checkout 5.2.2 Go back to the |ns3| root directory and patch the |ns3| code with the appropriated ``ofswitch13`` patch available under the ``ofswitch13/utils/`` directory (check for the correct |ns3| version): .. code-block:: bash $ cd ../../ - $ patch -p1 < contrib/ofswitch13/utils/ofswitch13-3_38.patch + $ patch -p1 < contrib/ofswitch13/utils/ofswitch13-3_39.patch This patch creates the new OpenFlow receive callback at ``CsmaNetDevice`` and ``VirtualNetDevice``, allowing OpenFlow switch to get raw packets from these devices. The module also brings a ``csma-full-duplex`` patch for improving CSMA connections with full-duplex support.